🎮 在 Python (Tkinter) 中构建动画 Hangman 游戏 — 步骤详解

发布: (2026年2月8日 GMT+8 12:17)
7 分钟阅读
原文: Dev.to

Source: Dev.to

🧰 我们将使用的工具

  • Python 3
  • Tkinter(GUI 框架)
  • ttk(样式化小部件)
  • sv‑ttk(现代的明暗主题)

📦 第一步 – 导入与依赖

import sys
import os
import random
import tkinter as tk
from tkinter import ttk, messagebox
import sv_ttk

为什么需要这些导入?

模块用途
random选择随机单词
tkinter构建 GUI
ttk更好看的部件
messagebox弹出对话框
sv_ttk现代亮/暗主题支持

🧠 第2步 – 辅助函数

正确加载文件(即使在打包后)

def resource_path(file_name):
    base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, file_name)

此辅助函数可确保在以后使用 PyInstaller 等工具打包应用时,文件路径仍能正常工作。

更新状态栏

def set_status(msg):
    status_var.set(msg)
    root.update_idletasks()

实时更新应用底部的状态信息。

🪟 第三步 – 创建主应用窗口

root = tk.Tk()
root.title("Animated Hangman Game")
root.geometry("700x850")
sv_ttk.set_theme("light")
  • Tk() 创建窗口。
  • geometry() 设置窗口大小。
  • sv_ttk.set_theme() 应用现代主题。

🌍 第4步 – 全局游戏状态

dark_mode_var = tk.BooleanVar(value=False)

word_list = ["PYTHON", "HANGMAN", "COMPUTER", "AI", "PROGRAMMING", "DEVELOPER"]

current_word = ""
guessed_letters = set()
wrong_guesses = 0
max_wrong = 6

UI‑绑定变量

display_word_var = tk.StringVar()
status_var = tk.StringVar(value="Ready")
lives_var = tk.StringVar(value=f"Lives: {max_wrong}")

StringVar 使 Tkinter 小部件在值变化时能够自动更新。

🌗 第5步 – 暗模式切换

def toggle_theme():
    bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
    fg = "white" if dark_mode_var.get() else "black"

    root.configure(bg=bg)

    for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]:
        style.configure(w, background=bg, foreground=fg)

    guess_entry.configure(background=bg, foreground=fg)
    hangman_canvas.configure(bg=bg)

当复选框切换时,动态切换背景和文字颜色。

🔁 第 6 步 – 开始新游戏

def start_new_game():
    global current_word, guessed_letters, wrong_guesses

    current_word = random.choice(word_list)
    guessed_letters = set()
    wrong_guesses = 0

    update_display_word()
    lives_var.set(f"Lives: {max_wrong}")
    draw_hangman(animated=False)

    set_status("New game started! Guess a letter.")

重置所有内容:选择一个新单词,清空已猜字母,并重置吊人图形。

🔤 第7步 – 更新单词显示

def update_display_word():
    display = " ".join(
        c if c in guessed_letters else "_" for c in current_word
    )
    display_word_var.set(display)

    # Win condition
    if "_" not in display:
        set_status("Congratulations! You guessed the word!")
        messagebox.showinfo("Winner!", f"You correctly guessed: {current_word}")

示例输出:

P Y T H O N

❓ 第8步 – 猜字母

def guess_letter():
    global wrong_guesses

    letter = guess_var.get().strip().upper()
    guess_var.set("")

验证输入

    if not letter or len(letter) != 1 or not letter.isalpha():
        set_status("Please enter a single letter.")
        return

防止重复猜测

    if letter in guessed_letters:
        set_status(f"You already guessed '{letter}'.")
        return

处理正确与错误的猜测

    guessed_letters.add(letter)

    if letter not in current_word:
        wrong_guesses += 1
        lives_var.set(f"Lives: {max_wrong - wrong_guesses}")
        draw_hangman(animated=True)

        # Game‑over logic
        if wrong_guesses >= max_wrong:
            messagebox.showinfo("Game Over", f"The word was: {current_word}")
            start_new_game()
            return

    update_display_word()

🎨 第 9 步 – 绘制绞刑人

基础结构(始终绘制)

def draw_hangman(animated=True):
    hangman_canvas.delete("all")

    # Scaffold
    hangman_canvas.create_line(50, 350, 250, 350, width=3)   # Ground
    hangman_canvas.create_line(150, 350, 150, 50, width=3)   # Pole
    hangman_canvas.create_line(150, 50, 300, 50, width=3)    # Top beam
    hangman_canvas.create_line(300, 50, 300, 100, width=3)   # Rope

身体部位(每次错误添加)

    parts = []

    if wrong_guesses >= 1:
        parts.append(("oval", 275, 100, 325, 150))   # Head
    if wrong_guesses >= 2:
        parts.append(("line", 300, 150, 300, 250))   # Body
    if wrong_guesses >= 3:
        parts.append(("line", 300, 180, 260, 220))   # Left arm
    if wrong_guesses >= 4:
        parts.append(("line", 300, 180, 340, 220))   # Right arm
    if wrong_guesses >= 5:
        parts.append(("line", 300, 250, 260, 300))   # Left leg
    if wrong_guesses >= 6:
        parts.append(("line", 300, 250, 340, 300))   # Right leg

渲染(可选动画)

    for i, (shape, *coords) in enumerate(parts):
        if animated:
            # Simple animation: draw each part after a short delay
            root.after(i * 200, lambda s=shape, c=coords: draw_part(s, *c))
        else:
            draw_part(shape, *coords)

def draw_part(shape, *coords):
    if shape == "oval":
        hangman_canvas.create_oval(*coords, width=3)
    elif shape == "line":
        hangman_canvas.create_line(*coords, width=3)

🎬 第10步 – 完成所有连接(UI布局)

style = ttk.Style()

# Top frame – title & theme toggle
top_frame = ttk.Frame(root, padding=10)
top_frame.pack(fill="x")

title_lbl = ttk.Label(top_frame, text="Hangman", font=("Helvetica", 24))
title_lbl.pack(side="left")

theme_chk = ttk.Checkbutton(
    top_frame,
    text="Dark mode",
    variable=dark_mode_var,
    command=toggle_theme
)
theme_chk.pack(side="right")

# Word display
word_frame = ttk.Frame(root, padding=10)
word_frame.pack(fill="x")
word_lbl = ttk.Label(word_frame, textvariable=display_word_var, font=("Consolas", 32))
word_lbl.pack()

# Canvas for the drawing
hangman_canvas = tk.Canvas(root, width=400, height=400, bg="white", highlightthickness=0)
hangman_canvas.pack(pady=10)

# Guess entry & button
guess_frame = ttk.Frame(root, padding=10)
guess_frame.pack(fill="x")
guess_var = tk.StringVar()
guess_entry = ttk.Entry(guess_frame, textvariable=guess_var, width=5, font=("Helvetica", 18))
guess_entry.pack(side="left", padx=(0, 5))
guess_btn = ttk.Button(guess_frame, text="Guess", command=guess_letter)
guess_btn.pack(side="left")

# Lives & status bar
status_frame = ttk.Frame(root, padding=5)
status_frame.pack(fill="x", side="bottom")
lives_lbl = ttk.Label(status_frame, textvariable=lives_var)
lives_lbl.pack(side="left")
status_lbl = ttk.Label(status_frame, textvariable=status_var, anchor="e")
status_lbl.pack(side="right", fill="x", expand=True)

# Start the first game
start_new_game()

root.mainloop()

🎉 完成!

运行脚本,切换暗模式,尽情享受使用标准 Python 库完全实现的动画猜词游戏体验。欢迎自行扩展功能,例如:

  • 使用更大的单词列表(从文件加载)。
  • 使用 winsoundpygame 添加音效。
  • 添加高分记录器。

祝编码愉快!

动画刽子手 – 代码演练

下面是该 Markdown 段落的清理后版本。所有原始代码、标题和内容均已保留,但已删除多余的 “Enter fullscreen mode / Exit fullscreen mode” 行,并且代码块已正确使用围栏标记。

animate_parts – 逐步绘制刽子手

def animate_parts(parts, idx=0):
    """Recursively draw each part of the hangman with a short delay."""
    if idx >= len(parts):
        return

    # Draw an oval (the head)
    if parts[idx][0] == "oval":
        hangman_canvas.create_oval(*parts[idx][1:], width=3)

    # Draw a line (any limb)
    else:
        x1, y1, x2, y2 = parts[idx][1:]
        hangman_canvas.create_line(x1, y1, x2, y2, width=3)

    # Wait a bit, then draw the next part
    hangman_canvas.after(400, lambda: animate_parts(parts, idx + 1))

🎛 第 11 步:UI 布局

主容器

main = ttk.Frame(root, padding=20)
main.pack(expand=True, fill="both")

画布

hangman_canvas = tk.Canvas(main, width=400, height=400, bg="white")
hangman_canvas.grid(row=1, column=0, pady=10)

输入与按钮

guess_var = tk.StringVar()

guess_entry = ttk.Entry(
    main,
    textvariable=guess_var,
    font=("Segoe UI", 16),
    width=5,
)
guess_entry.grid(row=4, column=0)

ttk.Button(main, text="Guess", command=guess_letter).grid(row=5, column=0)

▶️ 步骤 12:运行应用程序

start_new_game()
root.mainloop()

这将启动游戏循环并打开窗口。

🎉 最终结果

您现在拥有:

  • ✅ 动画绞刑人绘图
  • 🌗 明暗模式
  • 🎯 输入验证
  • 🧠 游戏状态处理
  • 🎨 现代 UI 样式

🚀 下一步想法

  • 添加难度等级
  • 从文件加载单词
  • 添加键盘输入
  • 记录高分
  • 打包为 .exe

预览

动画猜词游戏

所有代码片段均可直接复制到您的项目中。

0 浏览
Back to Blog

相关文章

阅读更多 »