Найти в Дзене
MrBk | Minecraft | Игры

Мои успехи в изучении Python!

Оглавление

Всем привет! В общем, некоторые из подписчиков и аудитории знают, что я изучаю Python на курсах от Алгоритмики (Онлайн), и вот недавно (В позапрошлую субботу) закончился первый "год" обучения, т.е. первый курс. Учился я на самом деле не год, а меньше, просто так говорится. Соответственно скоро начнётся второй курс, но а щас я просто хочу рассказать о своих успехах, перенесу две свои презентации в статью, которую я делал для уроков.

Первым делом на уроках потихоньку изучали всякие if, elif, else, def и т.д.
Рассказывали разные ошибки, которые могли бы мы допустить, объясняли принцип работы и что-куда ставить и всякое такое. Пройдусь просто по проектам, которые были больше других, по иерархии. Соответственно я всё ещё нубяра в этом языке, но что то да знаю.

Отмечу, что тут я буду рассказывать о коде в своём понимании. Я могу где то ошибиться, ведь я и год ещё не знаком с этим языком и являюсь новичком. Мои пояснения могут быть выражены так, как понимаю я. Для кого то это прозвучит неправильно, но в моей голове эти слова вызывают абсолютное понимание ситуации в том или ином моменте. Благодарю за понимание.

Отмечу ещё то, что я прикреплю код в текстовом файлике, но вы вряд-ли без знаний как им пользоваться запустите его, ибо я так и не понял, как сделать так, что бы его можно было активировать на компуктерах, не дорос малый ещё. Хотя, половину проектов тут точно запустить можно, но вот остальные под вопросом.

1 Проект:

-2

Простая "игра", где ты просто нажимаешь на старт, вписываешь свой никнейм и название оружия, а после смотришь кто победит. То ли ты, то ли враг, результат всегда разный. Правда, враг один, но всё же э то тестовый проект для развития навыков, а не то, что я развивал))

-3

Бой идёт пошаговым. Один удар от врага, один от героя. И так пока у кого нибудь не закончится здоровье, после игра либо выдаёт поражение, либо победу. Будем считать, что мы просто сразу перешли на финального босса, после которого ничего нет.

-4

Простенький проект, который помог развить использование классов, интеграции модулей и def, с которым у меня тогда была дикая запара, ибо я не понимал как он работает, вроде руки писали, а чё пишут - не понятно.

-5

В целом, тут всё написано. Это было интересно, да и в целом знаний дало новых) Думаю, ещё опишу код, который написал, может кому то пригодится.

-6

Первым делом я просто импортировал из модулей random и time все возможности, поставив звёздочку. Ну и был создан класс с названием Hero. Название то можно любое, но раз тут герои, то проще как раз таки использовать Hero :D

from random import *
from time import *
class Hero():

После был создан фрагмент кода с помощью def, где надобно создать атрибуты для самого квеста, т.е. урон, хп, дамаг и т.д, что будет использоваться потом. Соответственно прописывая def __init__, а в скобочках значения, которые хотелось бы использовать дальше, я смогу использовать hp и для врагов, и для союзников, но они будут абсолютно разными.

Далее просто приписываю self к каждому из значения в скобочках, кроме самого self. Таким образом они делаются "своими", т.е. их потом можно будет использовать много раз, можно даже сказать бесконечно, ибо использовав два раза self.nickname, то они друг другу мешать не будут.

def __init__(self, nickname, damage, hp, weapon,):
self.nickname = nickname
self.damage = damage
self.hp = hp
self.weapon = weapon
-7

После создаётся ещё одна не менее важная def штучка. info, обязательно (self) в скобочках. В общем то, тут всё основано на выводе данных о нашем герое, т.е. это подготовленный шаблон, который потом по мере кода будет изменятся. print() - вывод текста на экран. А так шо, я просто для красоты сделал эти print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^') и print('____________________________'), они не влияют ни на что, их можно убрать, но мне понравилось и так :)

Ну а далее для вывода здоровье используется как раз таки недавно созданный self.hp, просто вписываешь нужный текст, допустим: print('Здоровье', self.hp), в будущем этот параметр будет изменён на нужный, но как я говорил ранее - это шаблон. Тоже самое делается и с другими вещами. Урон, оружие, имя и т.д.

def info(self):
print('____________________________')
print(self.nickname)
print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^')
print('Информация о герое:')
print('Здоровье:', self.hp)
print('Урон:', self.damage)
print('Используемое оружие:', self.weapon)
print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^')
print('____________________________')

После опять создаётся def, но это последний в этой маленькой программе. В целом, это шаблон атаки. Благодаря нему можно будет производить драку. Тут создаётся новый параметр - vrag, который вписывается в скобочки для использования.

После этот враг становится как self, т.е. его можно использовать таким образом: vrag.hp, vrag.nickname и т.д. Таким образом можно вписать урон нашего героя self.dagame, которое будет отнимать здоровье у врага vrag.hp -= self.damage. Пока всё пустое, но чуть ниже они заполнятся цифрами.

После просто идёт информация о происходящем, мол, кто кого атаковал и сколько урона вообще нанесено. print(self.nickname,'аттаковал', vrag.nickname,'и нанёс', self.damage, 'урона'), а следом уже выводится само здоровье print('У', vrag.nickname, 'осталось', vrag.hp, 'здоровья')

def strike(self, vrag):
vrag.hp -= self.damage
print('----------------------------------------')
print(self.nickname,'аттаковал', vrag.nickname,'и нанёс', self.damage, 'урона')
print('У', vrag.nickname, 'осталось', vrag.hp, 'здоровья')
print('----------------------------------------')

Ну, а теперь def штуки кончились, пора идти дальше. Я хотел сделать что то вроде пролога, так что написал шо вообще происходит. Поэтому просто украсил плюсиками, а после вписал пролог. Он появится самым первым в игре, ибо стоит самым первым (def функции не активируются, если их не просить, так что они не проигрываются, а значит первым идёт как раз таки этот текст)

print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
print('Идя по подземелью, вы встретили плачущего мальчика, который был агрессивно настроен на вас. У вас не было выбора, на вас напали. Вы сожалеете о том, что вам придётся избить ребёнка?')
print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')

Ну а теперь, наконец-то, идут параметры персонажа. Помните, что было создано def __init__(self, nickname, damage, hp, weapon,):, в классе Hero? Поэтому создаём персонажа (Врага), который будет сражаться с нашим героем. Допустим, isaac = Hero т.е. обращаемся в классу Hero, а после в скобочках идёт незамысловатое обозначение параметров. Первое - имя. Почему? Потому что первым (Не считая self), в def функции с __init__ (Он как распространитель чумы, всем готов дать частичку себя), там мы писали в скобочках self, nickname, damage, hp, weapon. Т.е. сейчас мы забываем о self, а после в порядке выдаём значения. Первым - никнейм. У меня он 'Айзек', после идёт урон. Тут можно сделать фактическим, т.е. всегда одинаковым, но тогда исход тоже будет одинаковым, поэтому я подключил модуль random, дабы оттуда взять рандомайзер. В общем, написал randint(25,30) он будет выдавать каждый раз разное значение в начале программы, т.е. может быть 26, или 27. Следом, раз после дамага идёт hp, то значит пишем хп. Можно тоже сделать randint'ом, то я предпочёл фактическое хп, а именно 250. Ну и оружие, которое просто для визуального текста, мол, не просто же атаковал, а чем то! Ну вот и мой враг атакует меня 'Слёзы', т.е. слезами. Ну и следом просто пишу вывод этой информации на экран, беря шаблон info, который мы создали недавно в def, т.е. берём самого isaac, которому недавно присобачили параметры, а после isaac.info(), так он сам сможет понять что куда в информации сувать.

isaac = Hero('Айзек', randint(25,30), 250, 'Слёзы')
isaac.info()

Ну и не менее важным будет дело, так что это сделать тоже самое с нашим персонажем, но в начале дать выбор никнейма, а так же выбор названия оружия. Пояснять за maxim = Hero(history, randint(25, 75), 125, sword) не буду, ибо принцип работы такой же как и с isaac. А, ну только если я беру параметр history, потому что в начале нас спрашивают о никнейме, а вписав его в программу он запомнится и будет жить в переменной history, т.е. написав туда А, то значит ник будет А. Тоже самое с оружием sword.

history = input('---...Введите свой никнейм...---')
hp = 125
sword = input('Бой вот-вот начнётся? Но какое оружие вы выберете? (Это не влияет на хар-ки, просто дополнительный визуал)' )
maxim = Hero(history, randint(25, 75), 125, sword)
maxim.info()
-8

Ну а теперь надо сделать сражение, где они будут по очереди сражаться. Там я просто использовал цикл while, где проверялось для начала, мол, у двоих персонажей есть ли вообще здоровье? Иль они мертвы? Т.е. больше ли у них нуля хп, иль нет. Если больше, то программа засыпает на 3 секунды sleep(3), после идёт атака моего персонажа на врага, т.е. пишем maxim.strike(isaac), т.е. наш персонаж атакует благодаря шаблону strike, который сделали в def персонажа isaac. Потом программа идёт отдыхать на 3 секунды, проверяет есть ли у айзека здоровье. Если есть, то значит он атакует героя. Ну а если у кого то 0 хп, то идём ниже.

while maxim.hp > 0 and isaac.hp > 0:
sleep(3)
maxim.strike(isaac)
sleep(3)
if isaac.hp > 0:
isaac.strike(maxim)
-9

Если у кого то 0 хп или меньше нуля хп, то значит идёт чьё либо поражение. В общем, проверяется для начала есть ли у кого то 0 или меньше нуля здоровья, то идём внутрь программы, далее проверяется уже у кого здоровье то упало до нуля, а после уже и выписываются титры смерти. Т.е. если if maxim.hp <= 0 вдруг узнает, что у нашего героя вапщето есть здоровье, то он просто пошлёт нас вниз, прямиком к elif, который проверяет, мол, может isaac уже мёртв? Ну и если кто то из них мёртв, то выведётся шо Персонаж имя мёртв.

if maxim.hp <= 0 or isaac.hp <= 0:
if maxim.hp <= 0:
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('Персонаж' ,history, 'мёртв')
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!')
elif isaac.hp <= 0:
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('Персонаж Айзек мёртв')
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!')

Как то так и работает, вроде пояснил как смог. Ща ещё ролик прикреплю, где будет показана работа самой программы.

Код - Тык

__________________________________________________________________________________________

2 Проект:

Что-же... Теперь в свет идёт второй проект! Он хоть и меньше, но был немного сложнее в понимании, ибо мы сразу много чего нового затронули. В целом, теперь мы работали с изменением цвета фона, добавлением объектов, а так же работа с мышкой, т.е. что бы можно было кликать и что то изменять.

-10

В данной игре нужно было накликать нужное количество кубиков, дабы победить, но каждый раз нужный кубик менял своё положение, точнее, они то стояли на месте, но на нужном из них писалось ЖМИ, т.е. в данный момент он был тем, на который надо нажимать и зарабатывать очки. Был таймер, по истечению которого выводилось либо победа, либо поражение.

-11

Хотел описать код, но не смог. Да уж, как бы я его всё равно оставлю, но объяснить не смогу, я не на уровне учителя :D, для себя то я всё отлично понимаю, что и зачем. А вот как то передать в тексте я уже не смогу, ахах, может когда нибудь потом, когда буду лучше разбираться во всём этом.

Пока что я просто прикреплю видео, где показан функционал этой программы.

P.s. Я попытался ещё раз описать код, но всё же не смог, ахах, чёт тут я прям бессилен стал, хоть сам и понимаю, но слова подобрать никак не могу)) Ладно, идём дальше.

Код - Тык

__________________________________________________________________________________________

3 Проект:

-12

А вот в этом проекте мы уже делали разные проекты, где нужно было пройти препятствия и дойти до финиша. Почему разные? Ну, до этого мы делали одинаковые проекты, но теперь у нас будет разная задумка, дизайн и сами уровни, а вот до этого мы действовали полностью по шаблону задания.

-13

У меня на канале есть видео про эту игру, но я так же ещё сделаю письменный обзор. В целом, у меня есть презентация, но в видео всё же более подробно рассказано о мелочах.

-14

Прочитав данный слайд я могу сказать одно, так это почему я так решил. В целом, я около двух часов без остановки пытался улучшить свой результат! И у меня получилось, там вообще до милисекунд доходило дело, но в целом я смог улучшить свой рекорд до 7 секунд и 791 милисекунды.

-15

Небольшой сюжет и сюжетные повороты на этом слайде! Думаю, дам возможность картинкам рассказать о игре, а сам буду лишь чёт поддакивать из текста, пхах)

-16

Кстати, забавный факт! Вахтёрша на самом деле иллюзия. Т.е. откуда она могла появится и начать проходить сквозь стены? И почему она не подходит к персонажу? Всё просто - это страх. Он боится брать этот изумруд, сомнения... Из за этого у человека со сломанной психикой появляется силуэт того, кого он боялся. Т.е. ту, которая мешала ему сбежать и кричала на него, возможно у него были не лучшие отношения с ней. Поэтому при прикосновении к ней ничего не произойдёт, да, он просто сможет преодолеть свой страх и пройти дальше.

-17

В целом, я оставлю полную презентацию тут, можете её посмотреть. А я попробую разобрать код, ибо он мне вродь понятен))

Презентация: Алгоритмика и успехи - Тык

Презентация: Игра Мечта психа - Тык

Тут я импортировал всё из модуля pygame, создал окно 800 на 600 пикселей, дабы там потом расставлять объекты и персонажа, а так же создал таймер, потом нужен будет.

from pygame import *
window = display.set_mode((800, 600))
clock = time.Clock()

Ну и вновь пора заняться классами и def, без них вообще никуда, ибо они мега-важны! Они и крепят работу всего приложения.

Первым делом был создан класс Standart для спрайтов, т.е. для картинок. Там они будут корректироваться и выдадут шаблон для настройки расположения нужного спрайта. Для этого создаётся def с __init__, где описаны все параметры, которые будут использоваться. Имена, координата X и координата Y. Ну и self само собой.

После этот класс превращается в супер-класс! Ибо будет использоваться в других классах, да и просто так учитель сказал сделать, значит надо делать. Соответственно self.image = image.load(name) отвечает за то, что после загрузки картинки в проект, мы должны написать её имя для загрузки. Т.е. image.load и в скобочках имя картинки, после таких махинаций можно будет в коде потом прописать и внедрить в игру картинку. self.image = transform.scale(self.image, (33, 33)) отвечает за размер ВСЕХ спрайтов, которые будут загружены. Оно автоматически меняет их размер до 33 на 33 пикселя. self.rect = self.igame.get_rect() позволит сделать так, что расположение у каждого спрайта будет своё, а не одинаковое как с размером. Ну и ниже просто прописывает self к каждой координате. У нас 2Д игра, так что координаты две.

class Standart(sprite.Sprite):
def __init__(self, name, x, y):
super().__init__()
self.image = image.load(name)
self.image = transform.scale(self.image, (33, 33))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y

А эта штука понадобится для ресета игры.

def reset(self):
window.blit(self.image, self.rect)

А далее идёт много текста, который относится к новому классу, т.е. нужно создать ещё один класс, назвать его как нибудь и после создать там def, которая позволит создать управление нашему персонажу.

В целом, тут описано всё передвижение. Опишу начало, ибо потом просто повторяется текст, но меняется координата. Для начала создали класс, def, а после перешли уже к клавиатуре. Нужно же как то сделать так, что бы программа почувствовала нажатия на вашу клавиатуру, иль на МВА, ведь он клавиатура. В общем, нужно написать любое название, допустим klav, а после уже klav = key.get_pressed(), таким образом оно будет улавливать нажатия.

После нужно уже создать на какую кнопку надо нажимать, дабы он двигался, да и в целом куда должен двигаться. Для этого делаем проверку if klav[K_d] and self.rect.x < 760:, но что же это значит? Ну, во первых в квадратных скобочках [] пишется какая клавиша должна быть задействована для того, что бы что то менялось на экране. После уже идёт дополнительная проверка пикселей, вот у меня размер 800 пикселей вправо-влево, так вот если я не хочу шоб персонаж уходил за территорию программы, то должен написать пиксель, за который будет невозможно пройти. В моём случае - 760 пикселей. После, если кнопка реально нажата, то ему добавляется к расположению 5 пикселей, тем самым заставляя двигаться его в сторону. Зажав d он будет постоянно добавлять 5 пикселей к X, двигая нашего персонажа вправо. Т.е. self.rect.x += 5.

Ну и ещё одна проверка, так это спрайты. if sprite.spritecollide(self, walls, False), тут проверяется, мол, дотронулся ли персонаж до стены или нет. Если да, то его тепает обратно на 5 пикселей, т.е. персонаж стоит на месте, не давая пройти в стену. Это создаёт спрайту возможность быть материальным для нашего персонажа, так что таким образом он не сможет проходить сквозь стены, которые ему будут подставлены. Ниже будет повторение, но просто будут меняться буквы, возможность дохода до каких либо пикселей и куда будет шевелится персонаж, просто кнопки меняем и плюсы-минусы правильно расставляются.

class Player(Standart):
def update(self):
klav = key.get_pressed()
if klav[K_d] and self.rect.x < 760:
self.rect.x += 5
if sprite.spritecollide(self, walls, False):
self.rect.x -= 5
if klav[K_a] and self.rect.x > 0:
self.rect.x -= 5
if sprite.spritecollide(self, walls, False):
self.rect.x += 5
if klav[K_w] and self.rect.y > 0:
self.rect.y -= 5
if sprite.spritecollide(self, walls, False):
self.rect.y += 5
if klav[K_s] and self.rect.y < 565:
self.rect.y += 5
if sprite.spritecollide(self, walls, False):
self.rect.y -= 5

Теперь надо обработать врага. Ну, в моём случае он один, а именно - Вахтёрша, о которой я говорил выше. Да, она должна шевелится, так что ей надо тоже сделать передвижение, но вот она же бот... А значит наше управление с клавиатурой отпадает, ибо это тупо код с картинкой, а не существо с компьютером.

Ну, тут создаётся шаблон, который позволит каждому врагу менять свою скорость. Ну, у меня один враг будет, но мало ли я бы захотел несколько? В общем, тут просто создаются значения скорости, а именно: x_speed, y_speed, x_max, y_max, а после каждому из них выдаётся self и создаётся некая переменная, которая потом позволит им и дальше работать в программе.

class Enemy(Standart):
def setup(self, x_speed, y_speed, x_max, y_max):
self.x_speed = x_speed
self.y_speed = y_speed
self.x_max = x_max
self.y_max = y_max
self.x_start = self.rect.x
self.y_start = self.rect.y

В этой же def'ке было правило перемещения той Вахтёрши. Блин, чёт я не могу пояснить это, лол, ахах, это просто правила передвижения той великой иллюзии.

def update(self):
self.rect.x += self.x_speed
self.rect.y += self.y_speed
if self.rect.x > self.x_max or self.rect.x < self.x_start:
self.x_speed = - self.x_speed
if self.rect.y > self.y_max or self.rect.y < self.y_start:
self.y_speed = - self.y_speed

О, а я теперь надобно самого врага и героя загрузить, в плане их оболочку. В общем, спрайт. В первом созданном def я прописал такую штуку, как шаблон для спрайта. Так что первым в нём надо было писать спрайта. Допустим, у меня была картинка vrag, я должен был написать его имя, формат, а после координаты на поле. Вот враг появляется на 80/160 пикселях окна. После надо уже установить скорость, а так же до куда будет шевелится персонаж, а после возвращаться. В целом, 8 и 5 це скорость вверх/вниз. А двигается до 150 пикселя влево, а после обратно на точку старта 80, так же с 560.

А у hero просто написано где появится персонаж, на 10 пикселях по высоте и длине.

vrag1 = Enemy('vrag.png', 80, 160)
vrag1.setup(8, 5, 150, 560)
hero = Player('hero.png', 10, 10)

А потом идёт много текста с загрузкой спрайтов стен. Да, я не буду это вставлять, ибо это не имеет смысла. Просто скриншот прикреплю, а так там просто на специальном сайте разметил координаты и после вписывал их.

Но вот это надо выделить! Тут были созданы спрайты для гравия, т.е. snaryad1, после штука, которая даёт бонус, но в моём случае это ключ от двери, подобрав его - дверь открывается. bonus. И ловушка, т.е. тот же бонус, но анти-бонус, из за него игра закончится поражением, если его подобрать fake1.

Ну и для того шоб каждый раз не прописывать название каждого спрайта, то нужно бы и группы спрайтов создать. Т.е. все спрайты помещённые в нужную группу будут соединены вместе, не в плане визуала, а типо невидимой нитью. Подожжёшь - огонь по нитке перейдёт на другую. В общем, напишешь шо группа obman заканчивает игру при прикосновении - так и будет.

Ну и вот и были созданы во первых спрайты гравия, а после все стены, гравий и бонусы с антибонусами были перемещены в свои группы.

snaryad1 = Standart('grav4.png', 480, 25)
bonus = Standart('bonus.png', 100, 270)
fake1 = Standart('emerald.png', 535, 250)
obman = sprite.Group()
obman.add(fake1)
d1 = Standart('grav.png', 354, 150)
d2 = Standart('grav.png', 700, 460)
d3 = Standart('grav.png', 620, 340)
danger = sprite.Group()
danger.add(d1, d2, d3)
-19
walls = sprite.Group()
walls.add(s0 ,s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s27, s28, s29, s30, s31, s32, s33, s34, s35, s36, s37, s38, s39, s40, s41, s42, s43, s44, s45, s46, s47, s48, s49, s50, s51, s52, s53, s54, s55, s56, s57, s58, s59, s60, s61, s62, s63, s64, s65, s66, s67, s68, s69, s70, s71, s72, s73, s74, s75, s76, s77, s78, s79, s80, s81, s82, s83, s84, s85, s86, s87, s88, s89, s90, s91, s92, s93, s94, s95, s96, s97, s98, s99, s100, s101, s102, s103, s104, s105, s106, s107, s108, s109, s110, s111, s112, s113, s114, s115, s116, s117, s118, s119, s120, s121, s122, s123, s124, s125, s126)
finish = Standart('Untitled-2.png', 745, 430)

Кстати, фон ведь тоже нужен... Для этого я скачал фон из инета, а после установил его в игру, вписав название, а следующей строкой я сделал размер картинки под размер самого окна, шоб они были идеально пиксель в пиксель вставлены.

fon = image.load('clouds-g70141bcf4_1280.jpg')
fon = transform.scale(fon, (800, 600))

А, ну ещё ж надобно создать шрифт для надписей, которые будут. По типу победы, поражения и т.д. Он потом нужен будет, так что я создал шрифт, дал ему самый обычный шрифт без какого то смысла, размер до соточки повысил, ну и создал таймер, который как раз таки будет показывать за сколько была пройдена игра, ибо в этом и смысл - пройти быстро!

font.init()
shrift = font.Font(None, 100)
t_start = time.get_ticks()

Далее идёт большой цикл while, размер которого я ещё никогда не делал. В общем, я его разобью на части по if'ам, но всё это относится к одному циклу while.

Первым делом сам цикл while создаётся, там, если игра закрывается то всё ресается на места, да и вообще даёт возможность игре закрыться по нажатию на кнопку. Всё так сказать ресетается, создаётся и тому подобное. Если честно, то я чёт уже сам потупливаю шо тут.

while True:
window.blit(fon, (0,0))
for e in event.get():
if e.type == QUIT:
quit()
exit()
hero.update()
hero.reset()
bonus.reset()

Ну а после в этом цикле идёт работа со спрайтами, которые должны как то воздействовать на игрока. В целом, тут проверяется соприкоснулись ли спрайты героя и бонуса, ну, они указаны в скобках. Если туда впихнуть, допустим, группу walls, то значит все стены будут делать то шо идёт дальше, но нам нужен только бонус. Когда персонаж соприкасается с ним, то убирается стена (на скриншоте ниже показано стрелочкой бонус и стену, изумруд - бонус, а стена это картина с деревом). А сам бонус отправляется за экран, на тысячную координату. Почему его нельзя тупо удалить как и стену? В душе не знаю, я, видимо, решил поумничать. После этого идёт апдейт информации о врагах, дабы на экране что либо поменялось.

if sprite.collide_rect(hero, bonus):
walls.remove(s126)
bonus.rect.x = 1000
vrag1.reset()
vrag1.update()
-20

Ну и далее прописал снаряду (Это один гравий, который шевелится вниз-вверх! Ему пришлось делать так, что он опускается на 5 пикселей каждый раз, а доходя до нужного пикселя (160) он просто тепался обратно и повторял свои действия. А вот если персонаж дотронется до него, то игра не закончится, персонажа откинет в начало. Почему? Ну, помните, что в презентации я писал, что гравий нереален и в тех местах персонаж тупо задумывается? Ну так вот, в том месте герой вроде сдался, но этого мало, что бы сломить его, поэтому он продолжает путь.

Соответственно if snaryad1.rect.y > 160 проверяет, мол, где щас гравий. А если он дальше 160, то значит его тепает обрано на ноль. Ну и следом идёт проверка if sprite.collide_rect(hero, snaryad1):, если персонаж дотронулся до snaryad1, значит его тепает на место спавна, а именно на 10 пикселей по высоте и ширине.

snaryad1.reset()
snaryad1.rect.y += 5
if snaryad1.rect.y > 160:
snaryad1.rect.y = 0
if sprite.collide_rect(hero, snaryad1):
hero.rect.x = 10
hero.rect.y = 10
-21

А эта штука прорисовывает все эти группы на экране. Т.е. она просто выставляет стены, ловушки и т.д на экран, а так же восстанавливает финиш, вдруг с ним что то будет.

walls.draw(window)
danger.draw(window)
obman.draw(window)
finish.reset()

Это относится к поражению. Т.е. по сути тут докоснувшись до гравия мы тупо проиграем и игра закроется, придётся начинать с нуля. Тут это и прописано, что если спрайты hero и danger сойдутся воедино, то игра окончится и высветиться поражение! Т.е.
win = shrift.render('Поражение!', 1, (74, 0, 0)), а в скобокач указан цвет. После этого на экране появится ПОРАЖЕНИЕ!, после дисплей обновляется, и через 3 секунды игра закрывается. time.delay(3000) - 3 секунды, после которых идёт quit() закрытие и exit() выход, т.е. либо самому закрыть, либо само закроется.

if sprite.spritecollide(hero, danger, False):
win = shrift.render('Поражение!', 1, (74, 0, 0))
window.blit(win, (200, 150))
display.update()
time.delay(3000)
quit()
exit()

Ооо, а теперь финиш обсудим! Тут есть то, что весьма важно для игры, а именно таймер, который покажет нужное количество времени, за которое игра была пройдена.

В общем, в начале же я создал таймер, так вот тут он и используется. Просто берём t_finish = time.get_ticks() шоб получить время, а после незамысловатым образом вычисляется время, ну и после этого выводится надпись с победой, а так же время прохождения, которое делится на тысячу, дабы перевести в секунду. Ну и после этого через 3 секунды игра закроется.

if sprite.collide_rect(hero, finish):
t_finish = time.get_ticks()
total_time = t_finish - t_start
win = shrift.render('Победа за ' +str(total_time / 1000) + 'сек', 1, (0,0,0))
window.blit(win, (100, 150))
display.update()
time.delay(3000)
quit()
exit()

Кстати, есть ещё и про фейковый изумруд, который рядом с падающим гравием находится, так вот если его взять, то игра так же закроется, тупо как стоящие гравия.

if sprite.spritecollide(hero, obman, False):
haha = shrift.render('Изумруд - это ложь!', 1, (0,0,0))
window.blit(haha, (50, 150))
display.update()
time.delay(3000)
quit()
exit()

А, ну и ещё всегда работает вот такие две строчки, они просто обновляют постоянно экран. Если не написать это, то ничего не будет двигаться и меняться.

display.update()
clock.tick(60)

Код и файлики - Тык

4 Проект

Последний проект, который я делал на этом курсе. Это программа, не игра, а программа: Тест Руфье. В ней можно вести отсчёт по тесту, т.е. благодаря программы можно узнать здоровье сердца, не лазая в инет и не мучаясь с таймерами в реал-лайф.

-22

Тут нам объяснили и показали на практике, что можно писать код не только в кучу, а разделять его на разные файлики, где допустим таймер в отдельном файле для таймера, а виджеты в файле для виджетов, а после эти файлики с кодом импортировать в основной файл, где всё и происходит. Удобно и не позволит превратить код в мусорку, где набросано всё подряд.

-23

Я уже говорил, но этот проект оказался для меня самым лёгким. Видимо, потому что уже был опыт ранее, поэтому понимал шо да как. Я сам себе удивился, но если раньше я допускал ошибки, то тут у меня не было ошибок вообще, точнее, если они были, то это просто где то не дописанные буквы, но не критично. А вот раньше где то не то значение поставил, где то не тот метод, модуль и тому подобное. ПХах)

Ну а в коде всё шло так: Для начала пришлось импортировать из модуля PyQt5 разные штуки, которые будут помогать программе работать, точнее, заставлять её работать. Таймеры, время , виджеты, текст, кнопки и корректирование тех же виджетов под размер окна. Всплывающие окна, которые можно создавать. Ну и плюсом импорт данных из других файликов, а т.е. из instr, где в будущем будет инструкция, из second_win, где будет одно из окон, а в final_win будет последнее окно, просто уже заранее импортирую данные, шоб можно было потом забить на это и спокойно использовать всё оттуда.

from PyQt5.QtCore import Qt, QTime, QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QLineEdit, QVBoxLayout
from PyQt5.QtWidgets import QMessageBox
from instr import *
from second_win import *
from final_win import *
from PyQt5.QtGui import QFont

После создаётся самое приложение, потом окно, которое показывается благодаря третьей строки с .show(), а после этому окну уже поставилось имя Тест Руфье. Т.е. вверху окна будет написано его название, а не просто питон файл. А, ну и размер окна, я выбрал 400 на 600 пикселей, ибо большего и не надо.

app = QApplication([])
window = QWidget()
window.show()
window.setWindowTitle('Тест Руфье')
window.resize(400, 600)

Ну, а после уже создавались виджеты. Они не имели пока что функционала, просто надо же заранее разместить их, понять что будет, а потом уже работать с ними. Окон будет 3, т.е. начало - инструкция и запуск программы, после второе окно будет с записыванием данных, а вот третье выдаст результат.

Так что для начала виджеты первого экрана! Тут была создана надпись, а так же кнопка, на которую можно нажать, но после неё ничего не происходит, ибо ещё не дошли до того момента, что бы что то происходило с кнопкой.

e1_text1 = QLabel('Добро пожаловать в тест Руфье')
e1_text2 = QLabel(start_text)
e1_start = QPushButton('Начать')
-24

А так в файлик instr, который я загрузил в начале, написал туда инструкцию по использованию, которая будет как раз таки появляться на первом же экране. Правда, final_text будет использован во втором экране, но не суть важно.

start_text = '''Тест Руфье позволяет определить уровень здоровья сердца
1) Лечь и замерить пульс в состоянии покоя (15 сек)
2) Присесть 3 раз за 45 секунд
3) Замерить пульс в первые 15 секунд после приседания
4) Замерить пульс ещё раз после отдыха в 30 секунд
Впишите все результаты и получите индекс Руфье'''
final_text = '''Лягте на спину и замерьте пульс
в первые и последние 15 секунд таймера
запустите таймер сразу после приседаний
впишите результаты и отравьте их'''

Но с выводом потом разберёмся, для начала всё же надо создать все виджеты для каждого окна, шоб потом с ними поработать нормально. Так что в бой идёт второе окно, где будет самое большое количество виджетов в этой программе. Думаю, если почитать что тут написано, то будет понятно что делает каждая из этих строчек, тут дословно если переводить слова, то допустим QPushButton - нажатие кнопки, т.е. создаём кнопку с надписью.

Правда, вот QLineEdit создаёт строку, в которую можно писать свой текст, но остальное тут и так очевидно :D

e2_text1 = QLabel('Введите свой Возраст')
e2_age = QLineEdit()
e2_text2 = QLabel('Лягте и замерьте пульс (15 сек)')
e2_timer1= QPushButton('Запустить таймер (15 сек)')
e2_pulse1 = QLineEdit()
e2_text3 = QLabel('Присядьте 30 раз за 45 секунд')
e2_timer2 = QPushButton('Запустить таймер 45 секунд')
e2_text4 = QLabel(final_text)
e2_timer3 = QPushButton('Запустить таймер (1 мин)')
e2_pulse2 = QLineEdit()
e2_pulse3 = QLineEdit()
e2_final = QPushButton('Отправить результаты')
-25

Как я упомянул ранее, тут много виджетов. Не удобно будет каждый раз обращаться к каждому виджету, так что будет создан список, куда будут помещены все виджеты второго экрана!

ekran2 = [e2_text1, e2_age, e2_text2, e2_timer1,
e2_pulse1, e2_text3, e2_timer2, e2_text4,
e2_timer3, e2_pulse2, e2_pulse3, e2_final]

Ну и третий экран. Создаём нужные виджеты, в данном случае тут их всего два, Индекс Руфье, которые как раз таки покажет индекс, ну и Ваш уровень здоровья, который его и выведет. Пока это надписи, но потом они внатуре выведут данные. А, ну то что ниже, так это шрифт, размер текста и дополнительно жирный курсив, т.е. QFont.Bold.

e3_text1 = QLabel('Индекс Руфье:')
e3_text2 = QLabel('Ваш уровень здоровья:')
e3_text1.setFont(QFont('Times', 14, QFont.Bold))
e3_text2.setFont(QFont('Times', 14, QFont.Bold))
-26

Ну а теперь вывод виджетов на экран! Хе-хе, первым делом создаётся линия, по которой будет фиксироваться расстояние между виджетами, делая не просто всё чёрти как, а ровненько. Для этого и создаётся переменная line, которой присваивается QVBoxLayout(). А следом к этой линии добавляются виджеты первого экрана. alignment - это штука, которая позволяет выравнять что либо. Допустим, Qt.AlignJustify выравнивает текст по размер, а вот Qt.AlignCenter уже выравнивает виджеты к центру.

Следовательно потом идёт for i in ekran2:, но что это? А всё просто, она скрывает все виджеты второго экрана, не зря ведь создавали список ekran2 со всеми виджетами? Вот и скрываем их, что бы они не кучковались, да и не нужны они в данный момент. Поэтому циклом оно прогоняется по списку и каждому приписывается .hide(), что скрывает виджеты от глаз пользователя. А, ну и ещё группу ekran2 прогоняет по линии, т.е. делая все виджеты ровненькими на экране. Ну и следовательно виджеты 3 экрана для начала выравниваются по центру, а после уже скрываются. И да, виджеты с припиской e1 принадлежат первому экрану, e2 - второму, ну и e3 - третьему. Сделал себе для удобства.

line = QVBoxLayout()
line.addWidget(e1_text1, alignment = Qt.AlignJustify)
line.addWidget(e1_text2, alignment = Qt.AlignJustify)
line.addWidget(e1_start, alignment = Qt.AlignCenter)
for i in ekran2:
line.addWidget(i)
i.hide()
line.addWidget(e3_text1, alignment = Qt.AlignCenter)
line.addWidget(e3_text2, alignment = Qt.AlignCenter)
e3_text1.hide()
e3_text2.hide()

Ну, а теперь мне стоило бы добавить функционал кнопке в начале, что бы она вела на второй экран! Для этого создаётся функция def, что бы она без просьбы не активировалась. Там прописывается то, что все виджеты первого экрана скрывается, а все виджеты второго экрана показываются. Т.е. происходит некая замена виджетов. Но всё это активируется только тогда, когда условие e1_start.clicker.connect(start) будет выполнено. Т.е. был же создан виджет на первом экране в виде кнопки, так вот он назывался e1_start, и если на него нажать, то активируется функция def start, что указано в скобочках, а это обозначало одно - скрытие всех виджетов первого экрана и показ виджетов второго экрана.

def start():
e1_start.hide()
e1_text1.hide()
e1_text2.hide()
for i in ekran2:
i.show()
e1_start.clicked.connect(start)

Далее будет идти кусок кода, который будет добавлять функционал всем виджетам второго экрана, которым это надо. В общем, сейчас это всё будет относиться к одной функции def, а не к разным кускам кода. Просто я буду между этими частями писать объяснения, а то не удобно будет писать сверху, а код будет снизу, надо шоб он был в поле зрения текста.

Первым делом в def finish(): (Прикольная рожа на конце, либо мне только так кажется...) надо создать переменные тем виджетам, которые отвечают за вписывание своего текста. Дабы потом их можно было проверить и работать с ними дальше.

def finish():
age = e2_age.text()
p1 = e2_pulse1.text()
p2 = e2_pulse2.text()
p3 = e2_pulse3.text()

О, а тут надо отвлечься в другое место! А именно.. В вар тандер! Ой... Не, не то, в общем, надо отвлечься в файлик second_win. Там надо написать часть кода, которая будет проверять на ошибки, которые мог бы допустить пользователь. Т.е. пустые поля, с которыми нельзя работать, иль буквы, а не цифры и т.д. Так что сейчас отвлечёмся от функции def finish, и посмотрим на файлик second_win.

Тут мы создаём опять же def функции, только уже с названием check. Говорит за себя, да? В общем, туда добавляем недавно созданные переменные, которые как раз таки отвечают за все полоски, куда можно было вводить текст. Тут идёт сразу несколько проверок. Если все проверки прошли удачно, то ставится цифра 0, которая будет обозначать в программе, что всё сделано успешно и правильно. Но перед этим идут такие проверки:

if age == '' or p1 == '' or p2 == '' or p3 == '':

return 1

Оно проверяет, мол, есть ли вообще текст в полях? Вообще во всех полях, так что если текста нет, то выставляется цифра 1, которая потом в функции def finish будет обыграна и она будет показывать свою ошибку, о которой поговорим позже. Идём дальше:

for symbol in age+p1+p2+p3:

if not symbol in '0123456789':

return 2

Проверяет, мол, использованы ли ТОЛЬКО цифры в этих строчках? Если да, то идёт следующая проверка, а если есть буквы или другие знаки, помимо '0123456789', то значит выдаётся цифра 2, которая тоже будет значить свою ошибку. Ну, могу сказать, что for symbol in age+p1+p2+p3 просто скомкавывает все значения в сплошной текст, а после благодаря if идёт проверка этого symbol, мол, там только эти цифры? Как то так. Далее идёт последняя проверка:

if int(age) < 7:

return 3

return 0

Если пользователь младше 7 лет, то ему нельзя проходить тест, ибо тест рассчитан на детей с 8 лет. Как раз таки благодаря int переводится age из строки в число, а после сравнивается, мол, написанное число age меньше 7? Если да, то выдаётся ошибка 3, а если нет, то значит выдаётся цифра 0, которая показывает то, что ошибок нет.

def check(age, p1, p2, p3):
if age == '' or p1 == '' or p2 == '' or p3 == '':
return 1
for symbol in age+p1+p2+p3:
if not symbol in '0123456789':
return 2
if int(age) < 7:
return 3
return 0

Ну и нужна сама формула расчёта, которая покажет число индекса руфье. Ну, само здоровье оно не покажет, ибо это чуток в другом месте надо будет сделать, но хотя бы значения запомнит. Так что все переменные кроме age переводятся в числа int(), а после создаётся переменная index, которая благодаря расчёту индекса руфье (Тут я ничего не придумывал, это в интернете такая формула) рассчитывается сам индекс, а благодаря return index наша функция def formula получается этот результат как конечный, поэтому после можно будет использовать его для рассчёта.

def formula(p1, p2, p3):
p1 = int(p1)
p2 = int(p2)
p3 = int(p3)
index = (4 * (p1 + p2 + p3) - 200) / 20
return index

Ну а теперь пора вернуться к тому самому def finish, от которого мы на время отреклись. Так как def check для проверки был создан, то значит пора вписывать последствия этой проверки, если она будет пройдена не правильно.

Первым сценарием будет то, что ошибок вообще нет. Т.е. что делать программе дальше. В общем, если получен 0, а это показывает что ошибок нет, то тут придётся опять обратится к другому файлику finish_win, в котором будет написан весь расчёт для вывода третьего экрана.

proverka = check(age, p1, p2, p3)

Раз нам опять нужно отвлечься на другой файлик, то так и будет. Тут нужно создать сам результат и автоматическое применения на таблицу, что бы оно показывало уровень здоровья и сам результат. Почему тут такие числа я объяснять не буду, ибо и сам не знаю, всё же я списывал с таблицы Руфье, где все эти значения и написаны. Я просто прикреплю для вас эту таблицу.

if age == 13 or age == 14:
index -= 1.5
if age == 11 or age == 12:
index -= 3
if age == 9 or age == 10:
index -= 4.5
if age == 7 or age == 8:
index -= 6
if index >= 15:
return 'Низкий'
elif 11 <= index < 15:
return 'Удовлетворительный'
elif 6 <= index < 11:
return 'Средний'
elif 0.5 <= index < 6:
return 'Выше среднего'
elif index < 0.5:
return 'Высокий'
-27

Лады, раз таблица перенесена в код, то можно и выполнить сам переход на третий экран с результатами. Тут нужно для начала получить данные из функции formula, которую я писал выше, а после получить данные из функции result, которую я тоже писал выше. После того как они перенесены в переменную с любым названием, у меня это index и level, то идёт опять for i in ekran2:, которая просто скрывает все виджеты третьего экрана, а после благодаря .setText в скобочках прописывается новый текст этим виджетам третьего экрана, до этого они хоть и были с текстом, но не тем, который нужен. Там и будет использованы данные из index и level, правда, index'у надо обязательно приписать str, иначе результата не видать, мол, цифры со строками соединить нельзя, а благодаря str цифры станут строкой. Ну и после просто показывается третий экран с виджетами и информацией о себе.

if proverka == 0:
index = formula(p1, p2, p3)
level = result(int(age), index)
for i in ekran2:
i.hide()
e3_text1.setText('Ваш индекс Руфье:' + ' ' + str(index))
e3_text2.setText('Ваш уровень здоровья:' + ' ' + level)
e3_text1.show()
e3_text2.show()
-28

Ну а теперь обсудим те случаи, когда что то пошло не так. Сценарий №1. Неожиданно вы не заполнили все строки, которые вас попросили, поэтому вам выдали цифру 1, которая в этом случае выведет на экран всплывающие окно с текстом, мол, ЗАПОЛНИТЕ ВСЕ ЗНАЧЕНИЯ!

else:
if proverka == 1:
error = QMessageBox(text = 'Заполните все значения!')
-29

Сценарий №2. Вы решили написать не 8 в возраст, а 8 лет. Ну или просто написали какую нибудь точку, запятую, не важно, главное что это была не цифра. Тогда вылезет всплывающие окно, которое кстати создаётся то благодаря error = QMessageBox(text = 'Сюда текст'). В моём случае просят добавить цифры и попробовать снова.

elif proverka == 2:
error = QMessageBox(text = 'Можно писать только цифры! Попробуйте снова.')
-30

Сценарий №3. Вам меньше 7 лет, поэтому вам скажут об этом. Коротко и ясно.

elif proverka == 3:
error = QMessageBox(text = 'Вам меньше 7 лет. Тест предназначен для детей и подростков только от 7 лет')
-31

Кстати, каждый раз при любом сценарии будут происходить эти частички кода. Они дают имя окну, мол, ошибка ввода, а так же показывают эту ошибку благодаря show, ну а exec позволит закрыть её.

error.setWindowTitle('Ошибка ввода')
error.show()
error.exec_()

И благодаря этой строке кода всё это запустится. Все проверки и т.д. Когда вы нажмёте на кнопку ОТПРАВИТЬ РЕЗУЛЬТАТЫ, ныне в коде она называется e2_final, то значит пойдёт весь процесс проверок и вычисления.

e2_final.clicked.connect(finish)
-32

Вау! Кажется, мол, всё сделано! Но нет, ведь как можно было заметить по фотографиями, мы ещё и таймеры оказывается создавали, но на самом деле таймеры то не работают, это всё кнопка и текст... Т.е. нажав ничего не произойдёт, а значит надо это проработать.

Для этого нужно создать шаблон таймера:

time = QTime(0, 0, 0)
timer = QTimer()

Ну а теперь погнали разбираться с первым таймером! Тут придётся создать таймер на 15 секунд, для достижения этой цели придётся создать две новые def функции.

Ладно, для начала идёт функция def timeout_1():, т.е. функция для первого таймера, раз там единица. Первым же делом надо присвоить time и timer глобальность, т.е. сделать их глобальными переменными, что бы их можно было использовать не только внутри def, но и снаружи.

Следом надо сделать сам таймер, для этого надо взять ту самую переменную time и приписать ей time.addSecs(-1), т.е. каждый раз будет добавляться -1 секунда, что заставит уменьшаться время. Но для отображения нужно присвоить текст-шаблон тому виджету, который отвечает за текст на таймере, т.е. e2_timer1, ему мы приписываем шаблон отображения времени, а именно .setText(time.toString('hh:mm:ss')). А шоб таймер то не уходил в минус, а такое может быть, если не написать эту часть кода, надо в общем то просто сделать проверку, мол, сейчас ноль секунд иль нет? Если да, то таймер останавливается банальной фразой timer.stop(), а после скрывается командой .hide()

def timeout_1():
global time, timer
time = time.addSecs(-1)
e2_timer1.setText(time.toString('hh:mm:ss'))
if time == QTime(0, 0, 0):
timer.stop()
e2_timer1.hide()

А теперь надо сделать условия для старта этого таймера, поэтому создаём ещё одну функцию def start_timer_1():, там опять придётся использовать глобальные переменные, так что создаём их, а после сразу идёт проверка if time == QTime(0, 0, 0):, Т.е. для начала мы проверяем, не работает ли хоть один таймер? А то если будет работать два таймера, то время на одном из них будет уменьшатся в два раза больше, а если три, то в три раза больше, поэтому нельзя допускать запуска два таймера. Да и вообще-то тест заключается в поочередной проверке, поэтому таймеры нельзя использовать одновременно, для этого и проверяется, мол, QTime сейчас ноль иль нет? Если ноль, то присваиваем этому QTime 15 секунд. После создаётся шаблон показа и самое важное тут, так это соединить def timeout_1 и def start_timer_1, так что прописывается timer.timeout.connect(timeout_1), а после timer.start(1000), т.е. тысяча милисекунд, поэтому каждую секунду будет убираться одна секунда из таймера, там самым он заработает.

def start_timer_1():
global time, timer
if time == QTime(0, 0, 0):
timer = QTimer()
time = QTime(0, 0, 15)
e2_timer1.setText(time.toString('hh:mm:ss'))
timer.timeout.connect(timeout_1)
timer.start(1000)

Ну и создаётся возможность кликнуть и запустить функции, которые начнут работу таймера.

e2_timer1.clicked.connect(start_timer_1)

Остальные два таймера создаются так же, но меняются названия виджетов на нужные и время, а так они абсолютно одинаковы, так что повторять их не буду.

Кстати, в конце кода всегда стоят две строчки, дабы была возможность закрыть приложение и оно постоянно делает его ровным, т.е. если открыть его в полный экран, то все виджеты всё равно станут ровными.

window.setLayout(line)
app.exec_()

А так, на этом код закончился. Как и статья, ведь я рассказал о всём, что можно. Хотя я ещё оставлю видео с работой всего этого добра, где покажу и таймеры, и все три окна.

+ Код - Тык (Его даже запустить можно, я наконец-то смог найти способ перенести проект в exe файл :D)

Мне было забавно и прикольно переписать это всё в статью и вспомнить шо я делал, но думаю, пока на этом всё.

Так что благодарю за прочтение данной статейки! Надеюсь, что она вам понравилась и мы сможем увидится в других моих публикациях. Удачи!)

Так же вы можете оценить и другие мои статьи! Специально для удобности, я собрал все рубрики в одной статье. Из неё вы можете перейти в понравившуюся вам *Рубрику*

{}{=-А вот и сама статья - Тык-=}{}

=(}{}{)=МИНУТОЧКУ ВНИМАНИЯ!=(}{}{)=

Добавьте описание
Добавьте описание

У этого канала есть свой дискорд сервер! На нём, пока что, не много людей, но уже можно пообщаться с кем то, завести новые знакомства. В будущем, там будут разные голосования, опросы и тому подобное. А так же я иногда буду там писать новости и планы по дзен-каналу. Так что буду рад, если зайдёте)

{}{=-Сам дискорд сервер - Тык-=}{}

=(}{}{)=МИНУТОЧКУ ВНИМАНИЯ!=(}{}{)=

Да, давно такого не было, но конец статьи обновился! В общем, я открыл свой телеграмм-канал, где буду публиковать разную информацию. Допустим, ссылки на новые статьи и видео, которые вы можете прямо в телеграмме обсудить, там специальная кнопка будет. А так же причины отсутствия статей, даты выхода каких то рубрик, просто информация обо мне и разных ситуациях. В общем, буду и его развивать :D Попасть туда можно либо по ссылку ниже, либо по QR-коду выше.

{}{=-Сам телеграмм-канал - Тык-=}{}

#программирование #python #игры #приложения #мои успехи в изучении языка Python #интересное #код python #игра мечта психа #mrbk #канал