Вопрос: Как сделать так, чтобы пули на платформе pygame стреляли по вертикали?

Лучший ответ:
Процесс поиска ошибок называется "отладка". Как программист, вы первым делом должны овладеть этим навыком. И не важно, начинающий вы или нет.
В программировании вообще, в частности в языке Python, и уж тем более в игровом движке Pygame, удобно использовать консоль для вывода отладочной информации, а именно функцию print().
Я не буду, да и не смогу заниматься отладкой через ответы на этом ресурсе. Это ваша задача. Здесь иногда можно встретить телепатов и ясновидцев, но только в простых очевидных вопросах, где три строчки кода. Но с отладкой справитесь только вы. Потому что по сути это метод проб и ошибок. Нужно проверять, проверять, проверять, ставить эксперименты, пока ошибка не будет локализована.
Пример того, как можно было бы сделать отладку через консоль в вашем случае:for bullet in bullets:
        print("lastmove:", lastmove)
        if bullet.x > 0 and bullet.x < 1920:
            if lastmove == 1 or lastmove == 2:
                bullet.x += bullet.speed
        if lastmove == 3 or lastmove == 4:
                bullet.y += bullet.speed
        if bullet.x < 0 or bullet.x >1920:
            print("bullets.pop!!!")
            bullets.pop(bullets.index(bullet))
Но где, что и как вы будете проверять - вам решать.

Худший ответ:
Попробуйте добавить print(lastmove) перед движением пуль.
Добавлю пару непрошенных советов:if right == True: #заменить на if right:
if по определению срабатывает если условие равно True. Сравнение избыточно.if keys[pygame.K_a] and x>5:
        x -= speed
        left = True
        right = False
        up = False
       down = False
Очень много кода, и как следствие легко допустить ошибку. Используйте векторное представление направления. x и y будут иметь значения -1, 0 или 1.
https://www.pygame.org/docs/ref/math.html вам поможет удобно работать с такими векторами.direction = Vector2(0, 0)
    if keys[pygame.K_s] and y<1920 - tall -5:
        direction.y = 1
    if keys[pygame.K_w] and y>5:
        direction.y = -1
    if keys[pygame.K_a] and x>5:
        direction.x = -1
    if keys[pygame.K_d] and x<1920 - wide -5:
        direction.x = 1

    if direction.length() != 0:
        last_direction = direction
    pos += direction * speed;
    if keys[pygame.K_f]:
        if len(bullets) <= 100:
            bullets.append(Bullet(pos + gun_offset, (255, 0, 0), last_direction ))
    for bullet in bullets:
        bullet.pos += bullet.speed
        window_rect = pygame.Rect(0, 0, 1920, 1080) #можно определить глобально 1 раз
        if not window_rect.collidepoint(bullet.pos):
            bullets.pop(bullets.index(bullet))if keys[pygame.K_s] and y<1920 - tall -5:
Может быть чуть удобнее не смешивать регистрацию нажатий и контроля позиции. используйте window_rect.contains для этого непосредственно перед тем как изменить позицию игрока.
P.S.
Раньше pygame хотел обязательно int координаты при рисовании, поэтому Vector2 приходилось преобразовывать в пару int:def vec_to_int(vec):
    return (int(vec.x), int(vec.y))
pygame.draw.circle(screen, self.color, vec_to_int(self.pos), self.radius)
Но вроде как они это поправили, так что может это и не понадобится.