Как создать эффект полученного урона / красной виньетки в Pygame - PullRequest
1 голос
/ 28 мая 2019

Я делаю 2D игру на python, используя модуль pygame. Я хотел бы создать эффект красной виньетки / кровотечения, когда игрок получает урон в моей игре. Это наблюдается во многих играх сегодня, когда края экрана мигнут красным на секунду и быстро исчезнут.

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

Код, объявляющий несколько переменных:

bgX = 0
bgY = 0
damage = pygame.image.load("defensiveGameHUD.png").convert_alpha()
dimensions = [1920,1080]

Тогда у меня есть это в основном цикле моей игры:

win.blit(background,(0,0))

if dimensions[0] != 4020:
    dimensions[0] += 30
    bgX -= 15
if dimensions[1] != 4600:
    dimensions[1] += 40
    bgY -= 20

if dimensions[1] != 4600:
    screenDamage = pygame.transform.scale(damage, dimensions)
    win.blit(screenDamage, (bgX, bgY))
else:
    screenDamage = None

Это просто анимация, которая масштабирует изображение, однако масштабирование некорректно, и это очень дорого обходится с точки зрения производительности.

def smmothstep(edge0, edge1, x):
    t = min(1, max(0, (x - edge0) / (edge1 - edge0)))
    return t * t * (3.0 - 2.0 * t)

def gen_damage_image(scale, source):
    dest = source.copy()
    img_size = dest.get_size()
    for i in range(img_size[0]):
        for j in range(img_size[1]):
            fx = smmothstep(0, img_size[0]/2*scale, min(i, img_size[0]-i))
            fy = smmothstep(0, img_size[1]/2*scale, min(j, img_size[1]-j))
            color =  dest.get_at((i, j))
            fade_color = [int(255 - (1-fx*fy)*(255 - c)) for c in color]
            dest.set_at((i, j), fade_color)
    return dest

def tintDamage(surface, scale):
    i = min(len(dmg_list)-1, max(0, int(scale*(len(dmg_list)-0.5))))
    c.blit(dmg_list[i], (0, 0), special_flags = pygame.BLEND_MULT)

damage = pygame.image.load("defensiveGameHUD.png").convert_alpha()
max_dmg_img = 10
dmg_list = [gen_damage_image((i+1)/max_dmg_img, damage) for i in range(max_dmg_img)]


start_time = 0
tint = 0
damage_effect = False

1 Ответ

1 голос
/ 28 мая 2019

Тонировать экран красным можно, набрав pygame.Surface.fill(), установив special_flags = BLEND_MULT.
Следующая функция «окрашивает» всю поверхность красным, на scale изОт 0 до 1. Если scale равно 0, поверхность не тонируется, а если scale равно 1, вся поверхность окрашивается в (красный) цвет (255, 0, 0):

def tintDamage(surface, scale):
    GB = min(255, max(0, round(255 * (1-scale))))
    surface.fill((255, GB, GB), special_flags = pygame.BLEND_MULT)

Функция должна быть вызвана непосредственно перед pygame.display.flip() или pygame.display.update():

например,

tintDamage(win, 0.5)
pygame.display.flip()

Обратите внимание, что special_flags = BLEND_MULT также можно установить при использовании pygame.Surface.blit () :

win.blit(damage, (bgX, bgY), special_flags = pygame.BLEND_MULT)

Или даже оба эффекта могут быть объединены.


Это не совсем тот эффект, который я искал [...] Мне бы хотелось, чтобы этот эффект сортировал себя как внутри, так и снаружи, ...

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

Но вы можете предварительно рассчитать различные поверхности повреждения в зависимости от масштаба эффекта:

def smmothstep(edge0, edge1, x):
    t = min(1, max(0, (x - edge0) / (edge1 - edge0)))
    return t * t * (3.0 - 2.0 * t)

def gen_damage_image(scale, source):
    dest = source.copy()
    img_size = dest.get_size()
    for i in range(img_size[0]):
        for j in range(img_size[1]):
            fx = smmothstep(0, img_size[0]/2*scale, min(i, img_size[0]-i))
            fy = smmothstep(0, img_size[1]/2*scale, min(j, img_size[1]-j))
            color =  dest.get_at((i, j))
            fade_color = [int(255 - (1-fx*fy)*(255 - c)) for c in color]
            dest.set_at((i, j), fade_color)
    return dest

damage = pygame.image.load("defensiveGameHUD.png").convert_alpha()
max_dmg_img = 10
dmg_list = [gen_damage_image((i+1)/max_dmg_img, damage) for i in range(max_dmg_img)]

tintDamage выберите изображение повреждения в списке, в зависимости отмасштаб:

def tintDamage(surface, scale):
    i = min(len(dmg_list)-1, max(0, int(scale*(len(dmg_list)-0.5))))
    c.blit(dmg_list[i], (0, 0), special_flags = pygame.BLEND_MULT)

Эффект внутрь / наружу может быть достигнут с помощью функции синуса.См. Пример, который запускает эффект при нажатии x :

run = True
start_time = 0
tint = 0
damage_effect = False
while run:
    clock.tick(60)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_x:
                damage_effect = True
                tint = 0

    win.fill((196, 196, 196))

    # [...]

    if damage_effect:
        scale = math.sin(tint)
        tintDamage(win, scale)
        tint += 0.1
        damage_effect = scale >= 0
    pygame.display.flip()

Поскольку вычислениеизображения очень медленные, я предоставляю решение, которое генерирует масштабную маску для изображения 20x20.Маска масштабируется до размера изображения повреждения и смешивается с изображением повреждения:

def gen_damage_image(scale, source):
    scale_size = (20, 20)
    scale_img = pygame.Surface(scale_size, flags = pygame.SRCALPHA)
    for i in range(scale_size[0]):
        for j in range(scale_size[1]):
            fx = smmothstep(0, scale_size[0]/2*scale, min(i, scale_size[0]-i))
            fy = smmothstep(0, scale_size[1]/2*scale, min(j, scale_size[1]-j))
            fade_color = [int(max(0, 255 - (1-fx*fy)*255)) for c in range(4)]
            scale_img.set_at((i, j), fade_color)
    dest = source.copy()
    scale_img = pygame.transform.smoothscale(scale_img, dest.get_size())
    dest.blit(scale_img, (0, 0), special_flags = pygame.BLEND_ADD)  
    return dest
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...