Мышь: прицел и выстрел
Мышь — это не только кнопки, но и точные координаты. Научимся стрелять туда, куда смотрит игрок, и наводить объекты на курсор.
Суть: pygame.mouse.get_pos() даёт координаты курсора, а событие MOUSEBUTTONDOWN — клик. Из разницы между курсором и героем считается направление выстрела.
Во многих играх — шутерах, стратегиях, кликерах — главное устройство ввода это мышь. В Pygame она читается похоже на клавиатуру: есть события (клик, отпускание кнопки) и есть состояние (где курсор прямо сейчас). pygame.mouse.get_pos() возвращает (x, y) курсора в окне, а событие MOUSEBUTTONDOWN сообщает о клике.
Самая частая задача — «выстрелить в сторону курсора». Идея простая: у нас есть позиция героя и позиция мыши. Вектор от героя к мыши — это разность координат: (mouse_x - hero_x, mouse_y - hero_y). Эта стрелка показывает направление. Если её нормализовать (сделать длину равной 1) и умножить на скорость пули — получим скорость, летящую точно в курсор. Никакой тригонометрии, чистая геометрия векторов.
Как работает под капотом
Разность двух точек даёт вектор направления. Нормализация делит его на свою длину, оставляя только направление. Умножение на скорость задаёт, как быстро лететь:
мышь .(mx, my)
^
\ направление = (mx-hx, my-hy)
\
\ нормализуем -> длина 1
\ умножаем на SPEED -> скорость пули
герой .(hx, hy)
bullet_vel = normalize(мышь - герой) * SPEED
Мышь и выстрел в pygame (читаем):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
mx, my = event.pos # куда кликнули
direction = pygame.math.Vector2(mx - hero.x, my - hero.y)
if direction.length() > 0:
direction = direction.normalize()
bullet_vel = direction * 500 # пикс/сек
spawn_bullet(hero.pos, bullet_vel)Расчёт направления пули — чистая математика. Проверим без графики, как нормализуется вектор к курсору. Попробуй сам:
import math
def aim(hero, mouse, speed):
dx = mouse[0] - hero[0]
dy = mouse[1] - hero[1]
dist = math.hypot(dx, dy)
if dist == 0:
return (0.0, 0.0)
return (dx / dist * speed, dy / dist * speed)
hero = (400, 300)
for mouse in [(400, 0), (700, 300), (400, 600)]:
vx, vy = aim(hero, mouse, 500)
print(f"курсор {mouse}: скорость пули ({vx:.0f}, {vy:.0f})")Поворот спрайта вслед за прицелом
Часто хочется, чтобы спрайт (пушка, кораблик, стрелка) визуально смотрел в сторону курсора. Угол наводки берётся из того же вектора направления: у Vector2 есть свойство angle_to и метод as_polar, дающий угол напрямую. Этот угол передают в pygame.transform.rotate, и картинка поворачивается. Важная тонкость: при повороте размер картинки меняется, поэтому центр нужно сохранять через get_rect(center=старый_центр), иначе спрайт будет «гулять».
Ещё один совет — не пересоздавай повёрнутую картинку каждый кадр, если угол не изменился: поворот это недешёвая операция. Кешируй результат и обновляй только при смене направления. Связка «вектор к цели → угол → поворот спрайта» открывает целый класс механик: самонаводящиеся ракеты, башни, поворачивающиеся к врагу, стрелка-компас, указывающая на цель. Всё это вырастает из одной идеи — вектора разности двух точек, который ты уже умеешь считать вручную.
Та же связка «вектор к цели» решает целую россыпь задач, не только выстрел. Хочешь, чтобы враг летел к игроку, — берёшь вектор от врага к игроку. Хочешь, чтобы предмет убегал от курсора, — берёшь тот же вектор со знаком минус. Хочешь компас, указывающий на выход, — рисуешь стрелку вдоль этого вектора. Освоив один приём — разность двух точек, нормализация, умножение на скорость, — ты получаешь универсальный инструмент наведения на все случаи. Именно поэтому стоит понять его руками, как в запускаемом примере, а не просто скопировать: понимание превращает один рецепт в десяток разных механик, которые ты придумаешь сам.
Частые ошибки
- Нормализовать нулевой вектор — если курсор точно на герое, длина 0, и деление упадёт. Проверяй
length > 0. - Брать направление, но забыть умножить на скорость — пуля поползёт со скоростью 1 пиксель/сек.
- Считать координаты мыши относительно экрана, а не окна — get_pos даёт координаты внутри окна, это правильно.
Best practices
- Всегда проверяй длину перед нормализацией.
- Координаты клика бери из
event.pos— это точная точка клика. - Скорость пули храни как Vector2, обновляй позицию через
pos += vel * dt.
Итог: вектор «мышь минус герой», нормализованный и умноженный на скорость, — это твой универсальный прицел. Так стреляют, наводятся и преследуют.