【问题标题】:How to fade from one colour to another in pygame?如何在pygame中从一种颜色褪色到另一种颜色?
【发布时间】:2019-01-29 02:29:17
【问题描述】:

如何在 pygame 中从一种颜色淡入另一种颜色?我想慢慢地把一个圆圈的颜色从绿色变成蓝色到紫色到粉色到红色到橙色到黄色到绿色。我该怎么做?目前,我正在使用

def colour():
    switcher = {
        0: 0x2FD596,
        1: 0x2FC3D5,
        2: 0x2F6BD5,
        3: 0x432FD5,
        4: 0x702FD5,
        5: 0xBC2FD5,
        6: 0xD52F91,
        7: 0xD52F43,
        8: 0xD57F2F,
        9: 0xD5D52F,
        10: 0x64D52F,
        11: 0x2FD557,
    }
    return switcher.get(round((datetime.datetime.now() - starting_time).total_seconds()%11))

但这在颜色之间有很大的台阶,看起来很笨重。

【问题讨论】:

  • 当你得到解决方案时,请记得给有用的东西投票并接受你最喜欢的答案(即使你必须自己写),这样 Stack Overflow 才能正确存档问题。跨度>

标签: python colors pygame


【解决方案1】:

关键是简单地计算每个步骤您必须更改每个通道(a、r、g 和 b)多少。 Pygame 的 Color 类非常方便,因为它允许在每个通道上进行迭代,并且它的输入很灵活,所以你可以改变例如在下面的示例中,'blue'0x2FD596 仍然会运行。

这是一个简单的运行示例:

import pygame
import itertools

pygame.init()

screen = pygame.display.set_mode((800, 600))

colors = itertools.cycle(['green', 'blue', 'purple', 'pink', 'red', 'orange'])

clock = pygame.time.Clock()

base_color = next(colors)
next_color = next(colors)
current_color = base_color

FPS = 60
change_every_x_seconds = 3.
number_of_steps = change_every_x_seconds * FPS
step = 1

font = pygame.font.SysFont('Arial', 50)

running = True
while running:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    text = font.render('fading {a} to {b}'.format(a=base_color, b=next_color), True, pygame.color.Color('black'))

    step += 1
    if step < number_of_steps:
        # (y-x)/number_of_steps calculates the amount of change per step required to 
        # fade one channel of the old color to the new color
        # We multiply it with the current step counter
        current_color = [x + (((y-x)/number_of_steps)*step) for x, y in zip(pygame.color.Color(base_color), pygame.color.Color(next_color))]
    else:
        step = 1
        base_color = next_color
        next_color = next(colors)

    screen.fill(pygame.color.Color('white'))
    pygame.draw.circle(screen, current_color, screen.get_rect().center, 100)
    screen.blit(text, (230, 100))
    pygame.display.update()
    clock.tick(FPS)


如果您不想依赖帧速率而是使用基于时间的方法,您可以将代码更改为:

...
change_every_x_milliseconds = 3000.
step = 0

running = True
while running:

    ...

    if step < change_every_x_milliseconds:
        current_color = [x + (((y-x)/change_every_x_milliseconds)*step) for x, y in zip(pygame.color.Color(base_color), pygame.color.Color(next_color))]
    else:
        ...
    ...

    pygame.display.update()
    step += clock.tick(60)

【讨论】:

    【解决方案2】:

    您可以在从一种颜色到另一种颜色的所有值之间进行转换,方法是将其转换为 int、增加数字并将其转换回十六进制。然后你只需循环,直到你达到下一个值,如下所示:

    value1 = 0xff00ff
    value2 = 0xffffff
    increment = 1 # amount to decrease or increase the hex value by
    while value1 != value2:
        if value1 > value2:
            if int(value1)-increment < int(value2): # failsafe if the increment is greater than 1 and it skips being the value
                value1 = value2
            else:
                value1 = hex(int(value1)-increment)
        else:
            if int(value1)+increment > int(value2):
                value1 = value2
            else:
                value1 = hex(int(value1)+increment)
        code_to_change_colour(value1)
    

    查看 Prune 的编辑以获得更优雅的实现。请注意,code_to_change_colour(value1) 应更改为您更改程序中的颜色。增量将让您更改跳过的颜色数量。显然,这段代码需要被编辑成易于使用的方式:例如像def fade(value1, value2)这样的函数。


    从@Prune 编辑——因为代码在 cmets 中不能正常工作。

    请注意,您编写的大部分内容“仅仅是”循环控制。您已经知道开始值和停止值以及固定增量。这表明for 循环而不是while。考虑一下:

    value1 = int(0xff00ff)
    value2 = int(0xffffff)
    increment = 1 if value1 < value2 else -1
    
    for current in range(value1, value2, increment):
        code_to_change_colour(hex(value1))
    
    value1 = value2        
    

    【讨论】:

    • @Prune 啊,这是一种更好的编写方式。感谢您的不同观点:)
    【解决方案3】:

    如果您只想计算颜色(不使用任何表面),您可以这样做:

    首先,您需要确定溶解需要多长时间。您还需要存储原始颜色和最终颜色。最后,计算混合。我会为此创建一个类:

    import pygame
    import time
    
    class color_blend:
        def __init__(self, start_color, end_color, duration=1000):
            self.start_color = pygame.Color(start_color.r, start_color.g, start_color.b)
            self.current_color = pygame.Color(start_color.r, start_color.g, start_color.b)
            self.end_color = end_color
            self.duration = float(duration)
            self.start_time = color_blend.millis()
    
        # Return current time in ms
        @staticmethod
        def millis():
            return (int)(round(time.time() * 1000))
    
        # Blend any 2 colors
        # 0 <= amount <= 1 (0 is all initial_color, 1 is all final_color)
        @staticmethod
        def blend_colors(initial_color, final_color, amount):
            # Calc how much to add or subtract from start color
            r_diff = (final_color.r - initial_color.r) * amount
            g_diff = (final_color.g - initial_color.g) * amount
            b_diff = (final_color.b - initial_color.b) * amount
    
            # Create and return new color
            return pygame.Color((int)(round(initial_color.r + r_diff)),
                                (int)(round(initial_color.g + g_diff)),
                                (int)(round(initial_color.b + b_diff)))
    
        def get_next_color(self):
            # Elapsed time in ms
            elapsed_ms = color_blend.millis() - self.start_time
    
            # Calculate percentage done (0 <= pcnt_done <= 1)
            pcnt_done = min(1.0, elapsed_ms / self.duration)
    
            # Store new color
            self.current_color = color_blend.blend_colors(self.start_color, self.end_color, pcnt_done)
            return self.current_color
    
        def is_finished(self):
            return self.current_color == self.end_color
    
    # Blend red to green in .3 seconds
    c = color_blend(pygame.Color(255, 0, 0), pygame.Color(0, 255, 0), 300)
    while not c.is_finished():
        print(c.get_next_color())
    

    您可以轻松地对其进行修改以进行非线性混合。比如在blend_colorsamount = math.sin(amount * math.pi)

    (我不是 Pygame 专家 - 可能已经有一个内置函数。)

    【讨论】:

      【解决方案4】:

      将您的前景表面设置为旧颜色,覆盖新颜色的背景。使用set_alpha() 执行淡入淡出。完全使用新颜色后,将该表面作为前景,并为您的第三种颜色创建新背景。根据需要重复。

      This question 和其他对“fade”和set_alpha() 的引用应该可以让您完成这项工作。

      这足以让你感动吗?

      【讨论】:

      • 这可能比我的做法要好 :) 不过我的前进道路没有害处 :D
      • 何等伤害;这就是我们所有人的学习方式。我从这个问题中获得了两个新想法。
      【解决方案5】:

      我犹豫是否发布答案,因为我想出了与 Sloth 的答案几乎相同的答案,但我只想提一下 linear interpolation(简称 lerp,在 OpenGL/GLSL 中也称为 mix)。它通常用于混合两种颜色,但不幸的是 pygame 的 Color 类没有 lerp 方法,因此您必须定义自己的 lerp 函数并使用列表推导来插入 RGBA 值。

      这是从 Wikipedia 移植到 Python 的 lerp 函数(t 是权重,必须介于 0 和 1 之间):

      def lerp(v0, v1, t):
          return (1 - t) * v0 + t * v1
      

      现在您可以通过列表理解来处理两种颜色的 RGBA 值。

      color = [lerp(v0, v1, t) for v0, v1 in zip(color1, color2)]
      

      例如:

      >>> [lerp(v0, v1, .5) for v0, v1 in zip((0, 0, 0), (255, 255, 255))]
      [127.5, 127.5, 127.5]
      >>> [lerp(v0, v1, .25) for v0, v1 in zip((0, 0, 0), (255, 255, 255))]
      [63.75, 63.75, 63.75]
      

      如果你不需要 alpha 通道,你也可以使用 pygame 的 Vector3 类,它有一个 lerp 方法用于你的颜色,那么你只需要写:color = color1.lerp(color2, t)

      import itertools
      import pygame as pg
      from pygame.math import Vector3
      
      
      pg.init()
      screen = pg.display.set_mode((640, 480))
      clock = pg.time.Clock()
      
      color_cycle = itertools.cycle([
          Vector3(255, 0, 0),
          Vector3(0, 255, 0),
          Vector3(255, 255, 0),
          Vector3(0, 0, 255),
          ])
      
      color1 = next(color_cycle)
      color2 = next(color_cycle)
      color = color1
      start_time = pg.time.get_ticks()
      time_interval = 2000  # milliseconds
      t = 0
      
      done = False
      while not done:
          for event in pg.event.get():
              if event.type == pg.QUIT:
                  done = True
      
          now = pg.time.get_ticks()
          t = (now - start_time) / time_interval
      
          if t > 1:
              t = 0
              start_time = now
              color1, color2 = color2, next(color_cycle)
      
          color = color1.lerp(color2, t)  # Lerp the two colors.
      
          screen.fill(color)
          pg.display.flip()
          clock.tick(60)
      

      【讨论】:

        【解决方案6】:

        不久前我也遇到了类似的问题,我走上了疯狂学习微积分的路线 R = X,G = Y,B = Z

        然后写了一个程序来计算R0和R1(X0和X1)等之间的差异

        #Nick Poling
        #Fade Test V2
        
        import random
        #import mpmath
        #import sympy
        import time
        import pygame
        import shelve
        import sys
        from pygame.locals import *
        
        #Always Initialize
        pygame.init()
        
        #sympy contains function line_3d?
        #top level?
        #formated string?
        
        #Change Name Displayed in Corner
        MY_FONT = 'freesansbold.ttf'
        FONTSIZE_1 = 20
        pygame.display.set_caption('Fade Test V2')
        GAME_NAME = pygame.font.Font(MY_FONT, FONTSIZE_1)
        
        #Boards
        GAME_SCREEN_WIDTH = 900
        GAME_SCREEN_HEIGHT = 600
        
        Main_Game_Loop = 1
        
        def get_close_out():
            #Save_Game = get_Save_Game()
            pygame.quit()
            sys.exit()
        
        def get_Save_Game():
            #Save
            global Main_Game_Loop
            global GAME_SCREEN_COLOR
            global GAME_SCREEN_WIDTH
            global GAME_SCREEN_HEIGHT
            saveGameShelfFile = shelve.open('Fade Test V1')
            saveGameShelfFile['GAME_SCREEN_WIDTH'] = GAME_SCREEN_WIDTH
            saveGameShelfFile['GAME_SCREEN_HEIGHT'] = GAME_SCREEN_HEIGHT
        
        def get_Load_Game():
            global Main_Game_Loop
            global GAME_SCREEN_COLOR
            global GAME_SCREEN_WIDTH
            global GAME_SCREEN_HEIGHT
            try:
                #Load
                saveGameShelfFile = shelve.open('Fade Test V1')
                GAME_SCREEN_WIDTH = saveGameShelfFile['GAME_SCREEN_WIDTH']
                GAME_SCREEN_HEIGHT = saveGameShelfFile['GAME_SCREEN_HEIGHT']
            except:
                #Save
                #Save_Game = get_Save_Game()
                #Load
                saveGameShelfFile = shelve.open('Fade Test V1')
                GAME_SCREEN_WIDTH = saveGameShelfFile['GAME_SCREEN_WIDTH']
                GAME_SCREEN_HEIGHT = saveGameShelfFile['GAME_SCREEN_HEIGHT']
            GAME_SCREEN = pygame.display.set_mode((GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT),pygame.RESIZABLE)
            #By putting the GAME_SCREEN here you can make the resize doable with the press of a button
            #Does need to be un "#" to work when press "L"
        
        def get_Colors():
            #Colors
            global BLACK
            global WHITE
            BLACK = [0, 0, 0]
            WHITE = [255,255,255]
        
        def get_Main_Game():
            global Main_Game_Loop
            global GAME_SCREEN_COLOR
            global GAME_SCREEN_WIDTH
            global GAME_SCREEN_HEIGHT
            global BLACK
            global WHITE
            global Point_1
            global Point_2
            global Vector_1
            global Vector_2
            Colors = get_Colors()
            GAME_SCREEN_COLOR = BLACK
            #Load_Game = get_Load_Game()
            GAME_SCREEN = pygame.display.set_mode((GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT),pygame.RESIZABLE)
            while Main_Game_Loop == 1:
                GAME_SCREEN.fill(GAME_SCREEN_COLOR)
                Equation_Of_Lines_in_3D_Space = get_Equation_Of_Lines_in_3D_Space()
                for t in range(0,255):
                    XYZ_1 = [Point_1[0] + (Vector_1[0] * t), Point_1[1] + (Vector_1[1] * t), Point_1[2] + (Vector_1[2] * t)]
                    XYZ_2 = [Point_2[0] + (Vector_2[0] * t), Point_2[1] + (Vector_2[1] * t), Point_2[2] + (Vector_2[2] * t)]
                    GAME_SCREEN_COLOR = XYZ_1
                    GAME_SCREEN.fill(GAME_SCREEN_COLOR)
                    ticks = pygame.time.delay(5)
                    pygame.display.update()
                    for event in pygame.event.get():
                        if event.type == QUIT:
                            close_out = get_close_out()
                        elif event.type == pygame.VIDEORESIZE:
                            # There's some code to add back window content here.
                            surface = pygame.display.set_mode((event.w, event.h),pygame.RESIZABLE)
                            GAME_SCREEN_HEIGHT = event.h
                            GAME_SCREEN_WIDTH = event.w
                pygame.display.update()
        
        def get_Equation_Of_Lines_in_3D_Space():
            global Point_1
            global Point_2
            global BLACK
            global WHITE
            global Vector_1
            global Vector_2
            global LCM_X1
            global LCM_X2
            global LCM_Y1
            global LCM_Y2
            global LCM_Z1
            global LCM_Z2
            Point_1 = BLACK
            Point_2 = WHITE
            Vector_1 = []
            Vector_2 = []
            LCM_X1 = []
            LCM_X2 = []
            LCM_Y1 = [] 
            LCM_Y2 = [] 
            LCM_Z1 = []
            LCM_Z2 = []
            for i in range(0,3):
                #
                Delta_XYZ_1 = Point_2[i] - Point_1[i]
                Vector_1.append(Delta_XYZ_1)
                Delta_XYZ_2 = Point_1[i] - Point_2[i]
                Vector_2.append(Delta_XYZ_2)
            factors = get_factors()
        
        def get_factors():
            global num_1
            global num_2
            global Vector_1
            global Vector_2
            global LCM_XYZ_1
            global LCM_XYZ_2
            for i in range(1,7):
                if i == 1:
                    num_1 = Vector_1[0]
                    num_2 = 1
                elif i == 2:
                    num_1 = Vector_2[0]
                    num_2 = 2
                elif i == 3:
                    num_1 = Vector_1[1]
                    num_2 = 3
                elif i == 4:
                    num_1 = Vector_2[1]
                    num_2 = 4
                elif i == 5:
                    num_1 = Vector_1[2]
                    num_2 = 5
                elif i == 6:
                    num_1 = Vector_2[2]
                    num_2 = 6
                get_largest_and_lowest_common_factors(num_1)
            get_LCM_XYZ()
            Vector_1[0] = Vector_1[0] / LCM_XYZ_1[0]
            Vector_1[1] = Vector_1[1] / LCM_XYZ_1[0]
            Vector_1[2] = Vector_1[2] / LCM_XYZ_1[0]
            #
            Vector_2[0] = Vector_2[0] / LCM_XYZ_2[0]
            Vector_2[1] = Vector_2[1] / LCM_XYZ_2[0]
            Vector_2[2] = Vector_2[2] / LCM_XYZ_2[0]
        
        def get_largest_and_lowest_common_factors(x):
            global num_1
            global num_2
            global Vector_1
            global Vector_2
            global LCM_X1
            global LCM_X2
            global LCM_Y1
            global LCM_Y2
            global LCM_Z1
            global LCM_Z2
            #This function takes a number and puts its factor into a list
            for i in range(1, x + 1) or range(-x, x - 1, -1):
                try:
                    if x % i == 0:
                        if num_1 == Vector_1[0] and num_2 == 1:
                            LCM_X1.append(i)
                        elif num_1 == Vector_1[1] and num_2 == 3:
                            LCM_Y1.append(i)
                        elif num_1 == Vector_1[2] and num_2 == 5:
                            LCM_Z1.append(i)
                        elif num_1 == Vector_2[0] and num_2 == 2:
                            LCM_X2.append(i)
                        elif num_1 == Vector_2[1] and num_2 == 4:
                            LCM_Y2.append(i)
                        elif num_1 == Vector_2[2] and num_2 == 6:
                            LCM_Z2.append(i)
                except  ZeroDivisionError:
                    return 0
        
        def get_LCM_XYZ():
            global LCM_X1
            global LCM_Y1
            global LCM_Z1
            global LCM_X2
            global LCM_Y2
            global LCM_Z2
            global LCM_XYZ_1
            global LCM_XYZ_2
            #If 1 is 0
            check_1 = 0
            check_2 = 0
            check_3 = 0
            for i in range(0,3):
                if i == 0:
                    if LCM_X1 == [] and LCM_X2 == []:
                        check_1 = 1
                elif i == 1:
                    if LCM_Y1 == [] and LCM_Y2 == []:
                        check_2 = 2
                elif i == 2:
                    if LCM_Z1 == [] and LCM_Z2 == []:
                        check_3 = 3
            F_check = check_1 + check_2 + check_3
            if F_check == 1:
                LCM_X1.extend(LCM_Y1)
                LCM_X2.extend(LCM_Y2)
            elif F_check == 2:
                LCM_Y1.extend(LCM_X1)
                LCM_Y2.extend(LCM_X2)
            elif F_check == 3:
                if check_2 == 0:
                    LCM_Z1.extend(LCM_Y1)
                    LCM_Z2.extend(LCM_Y2)
                elif check_2 != 0:
                    LCM_X1.extend(LCM_Z1)
                    LCM_X2.extend(LCM_Z2)
                    LCM_Y1.extend(LCM_Z1)
                    LCM_Y2.extend(LCM_Z2)
            elif F_check == 4:
                LCM_X1.extend(LCM_Y1)
                LCM_X2.extend(LCM_Y2)
                LCM_Z1.extend(LCM_Y1)
                LCM_Z2.extend(LCM_Y2)
            elif F_check == 5:
                LCM_Y1.extend(LCM_X1)
                LCM_Y2.extend(LCM_X2)
                LCM_Z1.extend(LCM_X1)
                LCM_Z2.extend(LCM_X2)
            elif F_check == 6:
                LCM_X1.append(1)
                LCM_X2.append(1)
                LCM_Y1.append(1)
                LCM_Y2.append(1)
                LCM_Z1.append(1)
                LCM_Z2.append(1)
            LCM_X1 = set(LCM_X1)
            LCM_Y1 = set(LCM_Y1)
            LCM_Z1 = set(LCM_Z1)
            LCM_X2 = set(LCM_X2)
            LCM_Y2 = set(LCM_Y2)
            LCM_Z2 = set(LCM_Z2)
            LCM_XYZ_1 = list(set.intersection(LCM_X1,LCM_Y1,LCM_Z1))
            LCM_XYZ_2 = list(set.intersection(LCM_X2,LCM_Y2,LCM_Z2))
            LCM_XYZ_1.sort(reverse = True)
            LCM_XYZ_2.sort(reverse = True)
        
        
        Main_Game = get_Main_Game()
        

        【讨论】:

          【解决方案7】:

          Pygame 提供了pygame.Color 对象。该对象可以从各种参数(例如 RGBA 颜色通道、十六进制数字、字符串等)构造颜色。
          它还提供了方便的方法lerp,可以插入2种颜色:

          返回一个颜色,它是自身与 RGBA 空间中给定颜色之间的线性插值

          使用pygame.Color 对象和lerp 方法从颜色列表中插入颜色:

          def lerp_color(colors, value):
              fract, index = math.modf(value)
              color1 = pygame.Color(colors[int(index) % len(colors)])
              color2 = pygame.Color(colors[int(index + 1) % len(colors)])
              return color1.lerp(color2, fract)
          

          插值必须随时间变化。使用pygame.time.get_ticks 获取当前时间(以毫秒为单位):

          colors = ["green", "blue", "purple", "pink", "red", "orange", "yellow"]
          value = (pygame.time.get_ticks() - start_time) / 1000
          current_color = lerp_color(colors, value)
          

          另见Color - Lerp


          小例子:

          import pygame, math
          
          def lerp_color(colors, value):
              fract, index = math.modf(value)
              color1 = pygame.Color(colors[int(index) % len(colors)])
              color2 = pygame.Color(colors[int(index + 1) % len(colors)])
              return color1.lerp(color2, fract)
          
          pygame.init()
          window = pygame.display.set_mode((400, 300))
          clock = pygame.time.Clock()
          start_time = pygame.time.get_ticks()
          run = True
          while run:
              clock.tick(60)
              for event in pygame.event.get():
                  if event.type == pygame.QUIT:
                      run = False
                  if event.type == pygame.KEYDOWN:
                      start_time = pygame.time.get_ticks()
          
              colors = ["green", "blue", "purple", "pink", "red", "orange", "yellow"]
              value = (pygame.time.get_ticks() - start_time) / 1000
              current_color = lerp_color(colors, value)
          
              window.fill((255, 255, 255))
              pygame.draw.circle(window, current_color, window.get_rect().center, 100)
              pygame.display.flip()
          
          pygame.quit()
          exit()
          

          【讨论】:

            猜你喜欢
            • 2013-03-26
            • 2012-02-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多