如何在Pygame中以低速和闪烁的小角度绘制移动圆圈? [英] How to draw a moving circle in Pygame with a small angle at a low speed and blinking?

查看:23
本文介绍了如何在Pygame中以低速和闪烁的小角度绘制移动圆圈?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码在屏幕上移动两个球.第一个以 10 度的角度低速移动,具有良好的绘图质量.第二个球以 1 度的角度移动,为了尊重这个角度,速度必须高得多,并且闪烁很多,绘图不令人满意.有没有办法减慢第二个球的绘制速度,避免过度闪烁?

The code below moves two balls on the screen. The first one is moved with an angle of 10 degree at a low speed with a good drawing quality. The second ball is moved with an angle of 1 degree and in order for the angle to be respected, the speed must be much higher and the drawing is unsatisfactory with a lot of blinking. Is there a way to slow down the drawing of the second ball and avoid the excessive blinking ?

import pygame, sys, math
from pygame.locals import *

pygame.init()
DISPLAYSURF = pygame.display.set_mode((1000, 600))
pygame.display.set_caption('Bouncing Ball with position and angle')

# Set our color constants
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)

# Create the ball class
class Ball():
    def __init__(self,
                 screen,
                 color,
                 radius,
                 startX,
                 startY,
                 speed,
                 angle=45):
        super().__init__()
        self.screen = screen
        self.color = color
        rectSize = radius * 2
        self.rect = pygame.Rect(startX, startY, rectSize, rectSize)
        self.speed = speed
        self.angle = math.radians(angle)

    def update(self):
        delta_x = self.speed * math.cos(self.angle)
        delta_y = self.speed * math.sin(self.angle)
        self.rect = self.rect.move(delta_x, delta_y)

        if self.rect.right >= self.screen.get_width() or self.rect.left <= 0:
            self.angle = math.pi - self.angle

        if self.rect.top <= 0 or self.rect.bottom >= self.screen.get_height():
            self.angle = -self.angle

    def draw(self):
        '''
        Draw our ball to the screen with position information.
        '''
        pygame.draw.circle(self.screen, self.color, self.rect.center, int(self.rect.width / 2))

# Create a new Ball instance named 'myball'
myball = Ball(screen=DISPLAYSURF, color=YELLOW, startX=100, startY=100, radius=150, speed=8, angle=10)
mySmaLlAngleball = Ball(screen=DISPLAYSURF, color=YELLOW, startX=100, startY=500, radius=150, speed=58, angle=-1)

run = True
clock = pygame.time.Clock()

# Display loop
while run:
    # Handle events
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    # Update ball position
    myball.update()
    mySmaLlAngleball.update()

    # Draw screen
    DISPLAYSURF.fill(BLACK)
    myball.draw()
    mySmaLlAngleball.draw()
    pygame.display.update()
    clock.tick(30)

推荐答案

问题是造成的,因为 pygame.Rect 存储积分坐标:

The issue is caused, because pygame.Rect stores integral coordinates:

Rect 对象的坐标都是整数.[...]

The coordinates for Rect objects are all integers. [...]

delta_xdelta_y 的小数部分在 self.rect.move(delta_x, delta_y) 处丢失.

The fraction component of delta_x and delta_y is lost at self.rect.move(delta_x, delta_y).

您必须使用浮点数进行计算.添加一个属性 self.pos,它是一个包含 2 个组件的 tupe,用于存储球的中心点:

You have to use floating point numbers for the computation. Add an attribute self.pos, which is a tupe with 2 components an stores the center point of a ball:

self.pos = self.rect.center

计算具有最大浮点精度的位置:

Compute the position with the maximum floating point accuracy:

delta_x = self.speed * math.cos(self.angle)
delta_y = self.speed * math.sin(self.angle)
self.pos = (self.pos[0] + delta_x, self.pos[1] + delta_y)   

通过 self.rect.center>round()ed 位置.

Update the self.rect.center by the round()ed position.

self.rect.center = round(self.pos[0]), round(self.pos[1])

self.pos 是内部"位置并负责位置的精确计算.self.rect.center 是积分位置,负责拉球.self.pos 在每一帧中都略有变化.self.rect.center 仅当坐标的 a 分量改变了 1 时才会改变.

self.pos is the "internal" position and responsible for the exact computation of the position. self.rect.center is the integral position and responsible to draw the ball. self.pos slightly changes in each frame. self.rect.center changes only if the a component of the coordinate has changed by 1.

:

class Ball():
    def __init__(self,
                 screen,
                 color,
                 radius,
                 startX,
                 startY,
                 speed,
                 angle=45):
        super().__init__()
        self.screen = screen
        self.color = color
        rectSize = radius * 2
        self.rect = pygame.Rect(startX, startY, rectSize, rectSize)
        self.speed = speed
        self.angle = math.radians(angle)
        self.pos = self.rect.center

    def update(self):
        delta_x = self.speed * math.cos(self.angle)
        delta_y = self.speed * math.sin(self.angle)
        self.pos = (self.pos[0] + delta_x, self.pos[1] + delta_y)

        self.rect.center = round(self.pos[0]), round(self.pos[1])

        if self.rect.right >= self.screen.get_width() or self.rect.left <= 0:
            self.angle = math.pi - self.angle

        if self.rect.top <= 0 or self.rect.bottom >= self.screen.get_height():
            self.angle = -self.angle

    def draw(self):
        '''
        Draw our ball to the screen with position information.
        '''
        pygame.draw.circle(self.screen, self.color, self.rect.center, int(self.rect.width / 2))


使用此解决方案,您可以按比例增加每秒的触发器数 (clock.tick()) 并按相同比例降低速度.这会导致平稳移动而不会眨眼.


With this solution you can scale up the flops per second (clock.tick()) and scale down the speed by the same scale. This leads to a smooth movement without blinking.

这篇关于如何在Pygame中以低速和闪烁的小角度绘制移动圆圈?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆