"代码不仅是工具,更是艺术。"
一、为什么《黑客帝国》的数字雨如此经典?
1999年,电影《黑客帝国》(The Matrix)上映,那个绿色数字从天而降的画面,成为了影史最经典的视觉符号之一。
1.1 超越时代的视觉美学
导演沃卓斯基姐妹创造的这个数字雨效果,不仅仅是一个简单的动画,它代表了:
1.2 为什么它如此迷人?
心理学家分析,数字雨效果之所以令人着迷,是因为它触发了人类的几种本能反应:
1.3 数字雨的技术构成
从技术角度看,数字雨效果包含几个核心要素:
┌─────────────────────────────────────────┐│ 数字雨效果构成要素 │├─────────────────────────────────────────┤│ 1. 字符集:日文片假名、数字、字母 ││ 2. 颜色:荧光绿渐变(亮→暗) ││ 3. 运动:垂直下落 + 随机速度 ││ 4. 层次:多条独立的"雨滴轨迹" ││ 5. 透明度:尾迹渐隐效果 │└─────────────────────────────────────────┘
二、用Python创造你的数字雨
现在,让我们用Python重现这个经典效果!不需要复杂的图形库,只需要一个终端就能运行。
2.1 基础版数字雨(终端版)
这是最简单的实现,直接在终端中运行:
"""《黑客帝国》数字雨效果 - 终端版作者:鹏哥数个数运行环境:Python 3.x"""import randomimport osimport time# 清屏函数def clear_screen(): os.system('cls' if os.name == 'nt' else 'clear')# 字符集:数字、字母、日文片假名CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "アイウエオカキクケコサシスセソタチツテト" \ "ナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"def matrix_rain_terminal(width=80, height=24, speed=0.05): """ 终端版数字雨 width: 终端宽度 height: 终端高度 speed: 刷新速度(秒) """ # 初始化每列的位置 drops = [random.randint(-20, 0) for _ in range(width)] try: while True: clear_screen() # 生成每一行 for row in range(height): line = "" for col in range(width): if row == drops[col]: # 雨滴头部(最亮的字符) line += f"\033[1;32m{random.choice(CHARS)}\033[0m" elif drops[col] - 5 <= row < drops[col]: # 雨滴尾迹(较暗的字符) line += f"\033[0;32m{random.choice(CHARS)}\033[0m" else: line += " " print(line) # 更新雨滴位置 for i in range(width): drops[i] += 1 if drops[i] > height + random.randint(0, 10): drops[i] = random.randint(-10, 0) time.sleep(speed) except KeyboardInterrupt: clear_screen() print("数字雨已停止")if __name__ == "__main__": print("《黑客帝国》数字雨效果") print("按 Ctrl+C 停止") time.sleep(2) matrix_rain_terminal()
运行效果预览:
1 キ 5 A 9 0 ク 2 B 8 A エ 1 C 7 2 オ 0 D 6 B カ A E 5
2.2 进阶版数字雨(图形界面版)
如果你想要更炫酷的效果,可以使用 pygame 库:
"""《黑客帝国》数字雨效果 - Pygame图形版需要安装:pip install pygame"""import pygameimport random# 初始化pygamepygame.init()# 屏幕设置WIDTH, HEIGHT = 1200, 800screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("Matrix Digital Rain - 黑客帝国数字雨")# 颜色定义BLACK = (0, 0, 0)GREEN_BRIGHT = (180, 255, 180) # 亮绿色(头部)GREEN_NORMAL = (0, 255, 70) # 正常绿色GREEN_DARK = (0, 100, 30) # 暗绿色(尾迹)# 字体设置FONT_SIZE = 16font = pygame.font.SysFont("msmincho", FONT_SIZE, bold=True)# 字符集CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "アイウエオカキクケコサシスセソタチツテト" \ "ナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"class Drop: """雨滴类""" def __init__(self, x): self.x = x self.y = random.randint(-HEIGHT, 0) self.speed = random.randint(3, 8) self.length = random.randint(5, 20) self.chars = [random.choice(CHARS) for _ in range(self.length)] self.char_change = 0 def fall(self): """下落""" self.y += self.speed # 随机更换字符 self.char_change += 1 if self.char_change % 3 == 0: for i in range(len(self.chars)): if random.random() < 0.3: # 30%概率更换 self.chars[i] = random.choice(CHARS) # 重置位置 if self.y - self.length * FONT_SIZE > HEIGHT: self.y = random.randint(-200, -50) self.speed = random.randint(3, 8) self.length = random.randint(5, 20) self.chars = [random.choice(CHARS) for _ in range(self.length)] def draw(self, screen): """绘制""" for i, char in enumerate(self.chars): y_pos = self.y - i * FONT_SIZE if 0 <= y_pos < HEIGHT: # 头部最亮,尾迹渐暗 if i == 0: color = GREEN_BRIGHT elif i < 3: color = GREEN_NORMAL else: # 渐变暗 darkness = min(255, i * 15) color = (0, max(100, 255 - darkness), max(30, 100 - darkness // 3)) text = font.render(char, True, color) screen.blit(text, (self.x, y_pos))def main(): """主函数""" # 创建雨滴列 columns = WIDTH // FONT_SIZE drops = [Drop(x * FONT_SIZE) for x in range(columns)] clock = pygame.time.Clock() running = True print("按 ESC 或关闭窗口退出") while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False # 黑色背景(带轻微拖尾效果) fade = pygame.Surface((WIDTH, HEIGHT)) fade.fill(BLACK) fade.set_alpha(50) # 透明度 screen.blit(fade, (0, 0)) # 更新和绘制雨滴 for drop in drops: drop.fall() drop.draw(screen) pygame.display.flip() clock.tick(30) # 30帧/秒 pygame.quit()if __name__ == "__main__": main()
安装依赖:
pip install pygame
2.3 代码解析
让我们拆解一下数字雨的核心原理:
┌─────────────────────────────────────────────────────────────┐│ 数字雨原理图解 │├─────────────────────────────────────────────────────────────┤│ ││ 列1 列2 列3 列4 列5 列6 ││ ↓ ↓ ↓ ↓ ↓ ↓ ││ A キ 5 B 9 オ ← 雨滴头部(最亮) ││ 2 ク 2 C 8 カ ││ B エ 1 D 7 キ ← 雨滴中部 ││ 0 オ 0 E 6 ク ││ カ F 5 ││ ↑ ││ 雨滴尾迹(渐暗) ││ ││ 每一列是一个独立的"雨滴"对象 ││ - 有自己的下落速度 ││ - 有自己的字符序列 ││ - 到达底部后从顶部重新出现 │└─────────────────────────────────────────────────────────────┘
三、浪漫升级:下一场红色爱心雨
数字雨很酷,但如果是下爱心呢?让我们改造代码,创造一场浪漫的爱心流星雨!(有心的小伙伴,可以复现出来给自己女朋友惊喜!)
3.1 爱心雨代码
"""爱心流星雨效果用Python创造浪漫"""import pygameimport randomimport mathpygame.init()# 屏幕设置WIDTH, HEIGHT = 1000, 700screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("❤️ 爱心流星雨 ❤️")# 颜色BLACK = (10, 10, 20)RED_BRIGHT = (255, 100, 150)RED_NORMAL = (255, 50, 100)RED_DARK = (150, 30, 60)PINK = (255, 182, 193)# 字体FONT_SIZE = 20font = pygame.font.SysFont("simhei", FONT_SIZE, bold=True)# 爱心符号集合HEARTS = ["❤", "♥", "💕", "💖", "💗", "💓", "💝", "♡"]class HeartDrop: """爱心雨滴""" def __init__(self, x): self.x = x self.y = random.randint(-HEIGHT, -50) self.speed = random.uniform(2, 6) self.size = random.randint(14, 24) self.heart = random.choice(HEARTS) self.sway = random.uniform(0, 2 * math.pi) # 摆动相位 self.sway_speed = random.uniform(0.02, 0.05) self.sway_amount = random.uniform(0, 30) self.original_x = x self.rotation = 0 self.rotation_speed = random.uniform(-2, 2) self.opacity = 255 self.fade_speed = random.uniform(0.5, 2) def fall(self): """下落并摆动""" self.y += self.speed self.sway += self.sway_speed self.rotation += self.rotation_speed # 左右摆动效果 self.x = self.original_x + math.sin(self.sway) * self.sway_amount # 到达底部后重置 if self.y > HEIGHT + 50: self.y = random.randint(-100, -50) self.original_x = random.randint(50, WIDTH - 50) self.x = self.original_x self.speed = random.uniform(2, 6) self.heart = random.choice(HEARTS) self.opacity = 255 def draw(self, screen): """绘制爱心""" if 0 <= self.y < HEIGHT + 50: # 根据位置调整颜色(上面亮,下面暗) progress = self.y / HEIGHT if progress < 0.3: color = RED_BRIGHT elif progress < 0.7: color = RED_NORMAL else: # 渐隐效果 fade = max(0, 1 - (progress - 0.7) * 3) color = ( int(RED_DARK[0] + (RED_NORMAL[0] - RED_DARK[0]) * fade), int(RED_DARK[1] + (RED_NORMAL[1] - RED_DARK[1]) * fade), int(RED_DARK[2] + (RED_NORMAL[2] - RED_DARK[2]) * fade) ) # 使用系统字体渲染爱心 heart_font = pygame.font.SysFont("simhei", self.size, bold=True) text = heart_font.render(self.heart, True, color) # 绘制 screen.blit(text, (int(self.x), int(self.y)))class Particle: """粒子效果(闪烁的小星星)""" def __init__(self): self.x = random.randint(0, WIDTH) self.y = random.randint(0, HEIGHT) self.size = random.randint(1, 3) self.life = random.randint(30, 100) self.max_life = self.life self.color = random.choice([PINK, (255, 255, 200), (200, 200, 255)]) def update(self): self.life -= 1 if self.life <= 0: self.__init__() def draw(self, screen): alpha = self.life / self.max_life color = tuple(int(c * alpha) for c in self.color) pygame.draw.circle(screen, color, (self.x, self.y), self.size)def draw_background_gradient(): """绘制渐变背景""" for y in range(HEIGHT): # 从深蓝到黑色的渐变 color_val = int(20 + (y / HEIGHT) * 10) pygame.draw.line(screen, (color_val // 2, color_val // 3, color_val), (0, y), (WIDTH, y))def main(): """主函数""" # 创建爱心雨滴 hearts = [HeartDrop(random.randint(50, WIDTH - 50)) for _ in range(60)] # 创建背景粒子 particles = [Particle() for _ in range(50)] clock = pygame.time.Clock() running = True # 标题文字 title_font = pygame.font.SysFont("simhei", 48, bold=True) subtitle_font = pygame.font.SysFont("simhei", 24) print("爱心流星雨运行中...") print("按 ESC 或关闭窗口退出") while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False # 绘制渐变背景 draw_background_gradient() # 更新和绘制粒子 for particle in particles: particle.update() particle.draw(screen) # 更新和绘制爱心 for heart in hearts: heart.fall() heart.draw(screen) # 绘制标题 title = title_font.render("❤️ 爱心流星雨 ❤️", True, RED_BRIGHT) title_rect = title.get_rect(center=(WIDTH // 2, 50)) # 标题阴影 title_shadow = title_font.render("❤️ 爱心流星雨 ❤️", True, (50, 20, 30)) screen.blit(title_shadow, (title_rect.x + 2, title_rect.y + 2)) screen.blit(title, title_rect) # 绘制副标题 subtitle = subtitle_font.render("用Python创造的浪漫", True, PINK) subtitle_rect = subtitle.get_rect(center=(WIDTH // 2, 100)) screen.blit(subtitle, subtitle_rect) pygame.display.flip() clock.tick(60) # 60帧/秒 pygame.quit()if __name__ == "__main__": main()
3.2 爱心雨效果特点
3.3 运行截图示意
❤️ 爱心流星雨 ❤️ 用Python创造的浪漫 ❤ 💕 💖 💗 💓 💝 ♥ ❤ ♡ ❤ 💕 💖 💗 💓 💝 ♥ ♡ ❤ 💕 💖 💗 💓 💝 ♥ ♡
四、制作绚丽的烟花
4.1 烟花绽放效果
"""烟花绽放效果节日庆典"""import pygameimport randomimport mathpygame.init()WIDTH, HEIGHT = 1000, 700screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("🎆 烟花绽放 🎆")BLACK = (10, 10, 20)class Particle: def __init__(self, x, y, color): self.x = x self.y = y self.color = color angle = random.uniform(0, 2 * math.pi) speed = random.uniform(2, 8) self.vx = math.cos(angle) * speed self.vy = math.sin(angle) * speed self.life = random.randint(40, 80) self.max_life = self.life self.size = random.randint(2, 5) def update(self): self.x += self.vx self.y += self.vy self.vy += 0.1 # 重力 self.life -= 1 return self.life > 0 def draw(self, screen): alpha = self.life / self.max_life color = tuple(int(c * alpha) for c in self.color) pygame.draw.circle(screen, color, (int(self.x), int(self.y)), self.size)class Firework: def __init__(self): self.x = random.randint(100, WIDTH - 100) self.y = HEIGHT self.vy = random.uniform(-12, -15) self.color = random.choice([ (255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100), (255, 100, 255), (100, 255, 255) ]) self.exploded = False self.particles = [] def update(self): if not self.exploded: self.y += self.vy self.vy += 0.3 if self.vy >= 0: # 到达顶点 self.explode() else: self.particles = [p for p in self.particles if p.update()] return len(self.particles) > 0 or not self.exploded def explode(self): self.exploded = True for _ in range(100): self.particles.append(Particle(self.x, self.y, self.color)) def draw(self, screen): if not self.exploded: pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), 4) for p in self.particles: p.draw(screen)def main(): fireworks = [] clock = pygame.time.Clock() running = True frame_count = 0 while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: fireworks.append(Firework()) # 自动发射烟花 frame_count += 1 if frame_count % 60 == 0: # 每秒发射一个 fireworks.append(Firework()) screen.fill(BLACK) fireworks = [f for f in fireworks if f.update()] for f in fireworks: f.draw(screen) pygame.display.flip() clock.tick(60) pygame.quit()if __name__ == "__main__": main()
五、如何分享你的作品
5.1 保存为视频
"""将动画保存为视频文件需要安装:pip install opencv-python"""import cv2import numpy as npimport pygame# ... 你的绘制代码 ...# 设置视频写入器fourcc = cv2.VideoWriter_fourcc(*'mp4v')out = cv2.VideoWriter('matrix_rain.mp4', fourcc, 30.0, (WIDTH, HEIGHT))# 在主循环中while running: # ... 绘制代码 ... # 捕获帧 frame = pygame.surfarray.array3d(screen) frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) frame = np.rot90(frame, 3) # 旋转 frame = np.flip(frame, 1) # 翻转 out.write(frame)out.release()
结语
从《黑客帝国》的数字雨到浪漫的爱心流星,代码可以创造出令人惊叹的视觉效果。这不仅仅是编程技术的展示,更是艺术与科技的完美结合。
💡 给初学者的建议:
代码的世界没有边界,你的想象力就是极限。
完整代码已开源,关注公众号「鹏哥数个数」,回复"数字雨"获取所有源码文件!
作者简介:鹏哥数个数,热爱用代码创造美好事物,相信编程不仅是技术,更是艺术。
本文首发于微信公众号「鹏哥数个数」,转载请注明出处。
特别致谢:《黑客帝国》创作者们。