用pygame绘制完整弧线

-2 投票
1 回答
53 浏览
提问于 2025-04-13 20:57

我现在正在用Pygame编写一个吃豆人游戏,但在处理精灵的时候遇到了困难。我想画一个黄色的圆圈,上面有一个黑色的弧,就像一个没有切掉一块的派。我查了很多文档,但里面的内容对我来说太复杂了(我还在高中)。感谢你们的阅读和回复,祝好。

import pygame
from pygame.locals import *
from board import *
from math import *
import time
import sys

screen_width, screen_height = 660, 700
case_h = (screen_height-50)//32
case_w = screen_width//30

def create_window(longueur, largeur, titre, back_color):
    """
        Création de la fenêtre principale
        :param screen_width : la longueur de l'écran (en pixels)
        :type screen_width : int
        :param screen_height : la largeur de l'écran (en pixels)
        :type screen_height : int
        :param titre : nom donné à la fenêtre
        :type titre : str
    """
    # Création de la fenêtre
    windows = pygame.display.set_mode((longueur, largeur))
    windows.fill(back_color)
    # Nommage de la fenêtre
    pygame.display.set_caption('Pac-Man')
    return windows

def key_pressed():
    """
        Lecture du clavier du joueur
    """
    global run, direction
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                run = False
            if event.key == pygame.K_LEFT:
                direction = 'gauche'
            if event.key == pygame.K_RIGHT:
                direction = 'droite'
            if event.key == pygame.K_UP:
                direction = 'haut'
            if event.key == pygame.K_DOWN:
                direction = 'bas'

def draw_board(boards):
    """
        Affichage du plateau
        :param boards : matrice contenant les cases à afficher
        :type boards : list
    """
    for i in range(len(boards)):
        for j in range(len(boards[i])):
            if boards[i][j] == 1:
                pygame.draw.circle(windows, (255, 255, 0), (case_w*(j+0.5),
                                                    case_h*(i+0.5)), 4)
            if boards[i][j] == 2:
                pygame.draw.circle(windows, (255, 255, 0), (case_w*(j+0.5),
                                                    case_h*(i+0.5)), 6)
            if boards[i][j] == 3:
                pygame.draw.line(windows, (0, 0, 255), (case_w*(j+0.5), case_h*i),
                                (case_w*(j+0.5), case_h*(i+1)))
            if boards[i][j] == 4:
                pygame.draw.line(windows, (0, 0, 255), (case_w*j, case_h*(i+0.5)),
                                (case_w*(j+1), case_h*(i+0.5)))
            if boards[i][j] == 5:
                pygame.draw.arc(windows, (0, 0, 255), [case_w*(j-0.5), case_h*(i+0.5)
                                        , case_w, case_h], 0, pi/2, 1)
            if boards[i][j] == 6:
                pygame.draw.arc(windows, (0, 0, 255), [case_w*(j+0.5), case_h*(i+0.5)
                                        , case_w, case_h], pi/2, pi, 1)
            if boards[i][j] == 7:
                pygame.draw.arc(windows, (0, 0, 255), [case_w*(j+0.5), case_h*(i-0.5)
                                        , case_w, case_h], pi, (3*pi)/2, 1)
            if boards[i][j] ==8:
                pygame.draw.arc(windows, (0, 0, 255), [case_w*(j-0.5), case_h*(i-0.5)
                                        , case_w, case_h], (3*pi)/2, 2*pi, 1)
            if boards[i][j] == 9:
                pygame.draw.line(windows, (255, 255, 255), (case_w*j, case_h*(i+0.5)),
                                        (case_w*(j+1), case_h*(i+0.5)))

def pac_man_move(direction):
    """
        Création et affichage du Pac-man
        :param direction : direction du Pac-man
        :param direction : str
    """
    global posx, posy
    pygame.draw.rect(windows, (255, 255, 0), (case_w*posx, case_h*posy, case_w, case_h))
    if direction == 'gauche':
        posx -= 0.1
    elif direction == 'droite':
        posx += 0.1
    elif direction == 'haut':
        posy -= 0.1
    elif direction == 'bas':
        posy += 0.1

def draw_test():
    global posx, posy
    pygame.draw.arc(windows, (255, 255, 0), (case_w*posx, case_h*posy, case_w*(posx+1), case_h*(posy+1)), (11*pi)/6, pi/6, width=1)

run = True
posx = 15
posy = 24
direction = 'droite'
windows = create_window(screen_width, screen_height, 'Pac-Man', (0, 0, 0))
while run:
    windows.fill((0, 0, 0))
    draw_board(boards)
    pac_man_move(direction)
    key_pressed()
    if posx < 0:
        posx = 30
    elif posx > 30:
        posx = 0
    time.sleep(0.0125)
    pygame.display.update()

我试着用 pygame.draw.arc(...) 来画一个弧,并把 width=0,就像画矩形一样,如果 width=0,矩形会被填满,但弧却没有。

1 个回答

0

恭喜你完成这个项目!

问题是,Pygame的绘图功能没有其他绘图工具那么全面,像“饼图”这样的图形绘制选项根本就没有。

现在有几个解决办法:(1) 使用其他绘图工具库,比如Cairo、pyglet、matplotlib或者PIL(不确定最新的PIL是否也支持“饼图”)——然后把生成的像素复制到一个Pygame可以用的对象里;(2) 在图像处理软件中提前绘制好图像,然后在Pygame项目运行时从“.png”文件中加载这些图像(这可能是更简单的办法);(3) 如果你想更深入地了解“背后的工作原理”,可以用数学函数来绘制吃豆人形状,这样你能更好地控制,比如在填充区域添加渐变和其他颜色变化效果。

第三种方法需要一些三角函数的思考,以及一些基本的形状栅格化规则,这些都是学习的基础,能帮助你理解事物是如何运作的:基本思路是使用两个嵌套的循环来生成你想绘制的吃豆人的所有(x, y)坐标——对于每个坐标,你需要判断它是否应该被填充(如果要做更复杂的效果,可能还需要计算自定义颜色)。对于吃豆人形状来说,(x,y)需要在半径为“r”的圆内,并且在指定的角度范围之外,你需要用math.asinmath.acos来计算。

def pacman(surf,  total_r, min_ang):
    width, height = sc.get_size()
    cx, cy = width // 2, height // 2
    for y in range(height):
        for x in range(width):
            nx, ny = (x - cx) / cx, (y - cy) / cy
            r = (nx ** 2 + ny ** 2 ) ** 0.5
            alpha = math.degrees(math.asin(ny / r if r else 0.00001))
            if r < total_r and (abs(alpha) > min_ang or nx < 0) :
                color = (255, 255, 0)
            else:
                color = (0, 0, 0)
            surf.set_at((x,y), color)
    # pygame.display.flip()

传入一个表面(比如64x64像素,或者你想要的其他尺寸),或者直接把屏幕传过去进行测试——它会在“0”角度(从左到右的水平线)绘制一个吃豆人形状。

当然,这样每帧渲染图像太慢了:在游戏设置阶段绘制好各种吃豆人图像,然后把它们保存在不同的Pygame Surface对象中,游戏时可以用.blit来快速显示。

撰写回答