В рамках эксперимента по "разработке от сниппетов" понял, что нужно поменять файловую структуру, которая позволит положить рядом два сниппета, если они как-то связаны по смыслу. Использвоть просто числа в именах файлов нет смысла, потому что не получится вставить файл между p_1 и p_2, а использовать спецсимволы в именах скриптов на питоне тоже не очень удобно. Поэтоу растащил файлы по директориям.
Теперь это выглядит так:
И если мне нужно будет добавить сниппет между 1 и 2, то это будет выглядеть так:
Сделал сниппет, в котором шарик бегает за мышкой. Особенность этого сниппета в том, что шарик поворачивается в сторону мышки не сразу, а с заданной угловой скоростью. За счёт этого добавляется эффект инерции при перемещении по экрану.
Красная линия показывает направление вектора скорости.
importpygameaspgfrommathimportcopysignfrompygameimportQUIT,KEYDOWN,K_ESCAPEfrompygame.mathimportVector2frompygame.timeimportClockUPDATE_RATE=1/120MAX_UPDATE_RATE=1/20classFollower:def__init__(self,surface,position,speed):self.surface=surfaceself.position=Vector2(position)self.speed=speedself.direction=Vector2(0,1)self.angle_speed=90# Градусов в секундуself.color=(44,44,44)self.line_color=(244,22,22)self.radius=20defupdate(self,delta_time):mouse_position=Vector2(pg.mouse.get_pos())direction_to_mouse=(mouse_position-self.position).normalize()ifmouse_position.distance_to(self.position)>self.radius:angle=self.direction.angle_to(direction_to_mouse)ifabs(angle)>180:angle=-copysign((360-abs(angle)),angle)self.direction=self.direction.rotate(copysign(min(abs(angle),self.angle_speed*delta_time),angle))else:self.direction=direction_to_mouseself.position+=(self.direction*self.speed)*delta_timedefrender(self):pg.draw.circle(self.surface,self.color,self.position,self.radius)pg.draw.line(self.surface,self.line_color,self.position,self.position+self.direction*(self.radius+10),5)classGame:def__init__(self,surface):self.surface=surfaceself.bg_color=(220,220,220)self.follower=Follower(surface,(surface.get_width()/2,100),200)self.clock=Clock()self.time_bucket=0self.working=Truedefhandle_events(self):foreinpg.event.get():ife.type==QUITor(e.type==KEYDOWNande.key==K_ESCAPE):self.working=Falsedefupdate(self,delta_time):self.time_bucket+=delta_timewhileself.time_bucket>=UPDATE_RATE:self.time_bucket-=UPDATE_RATEself.follower.update(UPDATE_RATE)defdraw_direction(self):pg.draw.line(self.surface,(44,200,44),self.follower.position,pg.mouse.get_pos())defrender(self):self.surface.fill(self.bg_color)self.follower.render()self.draw_direction()pg.display.update()defrun(self):delta_time=0self.clock.tick()whileself.working:self.handle_events()self.update(min(delta_time,MAX_UPDATE_RATE))self.render()delta_time=self.clock.tick()/1000defmain():pg.init()Game(pg.display.set_mode((1920,1080),flags=pg.FULLSCREEN)).run()pg.quit()if__name__=="__main__":main()
В этом сниппете я сделал реализацию двух вариантов обработки времени в игровом цикле (GameLoop).
Если зажать кнопку мыши, то имитируется подвисание игры (пока кнопка зажата, не вызывается обновление). На экране в этот время вокруг курсора рисуется зелёный круг.
У приложения есть два режима: "медленный" и "быстрый". В медленном режиме время между вызовами update передаётся в метод "как есть" и используется для вычисления следующей позиции по формуле "скорость * время":
И при таком варианте шарик может пролетать через стену (в медленном режиме на видео чёрный фон).
Для "быстрого" режима есть минимально возможное значение delta_time. Для примера я выставил в одну секунду, но в реальном приложении поставил бы 1 / 20 секунды. И помимо этого, вызовы update идут не на всё время, а на отдельные кусочки UPDATE_RATE.
В этом сниппете я сделал загрузку изображения с диска и вращение вокруг центра экрана. Если нажать на какую-нибудь клавишу, то направление вращения меняется на противоположное.
Ключевой момент в этом сниппете в том, что выводить на экран нужно не просто картинку в заданных координатах, а Rect, который получается с заданным центром. Если этого не сделать, а просто вывести картинку так: