'How to draw a circle onto a surface and rotate it while moving?

I am trying to:

  1. draw a circle onto my window
  2. let it move AND rotate at the same time to simulate a rolling ball.

Appreciate anyone's solutions! Attached below is my code.

import pygame

#create win
W, H = 700, 700
win = pygame.display.set_mode((W, H))
pygame.display.set_caption("Rotating Ball Simul")

#color lib
BG = (20,20,20)
BLUE = (0,0,255)

#draw circle
ballX, ballY = 0, 0
ballW = 20
ballH = ballW

ball = pygame.draw.circle(win, BLUE, (ballX, ballY), ballW, 1)

def redraw_window():
  win.fill(BG)
  win.blit(ball, (ballX, ballY))
  pygame.display.update()

def main():
  
  run = True
  while run:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        run = False

    redraw_window()

if __name__ == '__main__':
  main()


Solution 1:[1]

A circle always looks the same when you rotate it. You must use a sphere image to see the rotation. Read about How do I rotate an image around its center using PyGame? and move the ball image and change the rotation of the image depending on the movement. The angle of rotation depends on the movement and the diameter of the ball:

angle -= (self.x - prev_x) / self.diameter * 180 / math.pi
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center = self.rect.center)

Minimal example:

repl.it/@Rabbid76/PyGame-FollowInGrid

import os
import math
import pygame

class MarbelSprite(pygame.sprite.Sprite):
    def __init__(self, x, ground, diameter, velocity, filename):
        pygame.sprite.Sprite.__init__(self)
        try:
            self.image = pygame.transform.smoothscale(pygame.image.load(filename).convert_alpha(), (diameter, diameter))
        except:     
            self.image = pygame.Surface((diameter, diameter), pygame.SRCALPHA)
            pygame.draw.circle(self.image, (255, 128, 0), (diameter // 2, diameter // 2), diameter // 2)
        self.original_image = self.image
        self.rect = self.image.get_rect(midbottom = (x, ground))
        self.diameter = diameter
        self.x = x
        self.velocity = velocity
        self.move_x = 0
        self.follow = None
        self.angle = 0
        
    def update(self, time, restriction):
        move_x = 0
        prev_x = self.x
        if self.move_x != 0:
            move_x = self.move_x * self.velocity * time
        elif self.follow:
            dx = self.follow.rect.centerx - self.x
            move_x = (-1 if dx < 0 else 1) * min(self.velocity * time, abs(dx))
        self.x += move_x
        self.x = max(restriction.left + self.diameter // 2, min(restriction.right - self.diameter // 2, self.x))
        self.rect.centerx = round(self.x)
        self.angle -= (self.x - prev_x) / self.diameter * 180 / math.pi
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center = self.rect.center)

pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()

ground_level = 220
object = MarbelSprite(window.get_rect().centerx, ground_level, 100, 0.4, 'BaskteBall.png')
follower = MarbelSprite(window.get_width() // 4, ground_level, 50, 0.2, 'TennisBall.png')
all_sprites = pygame.sprite.Group([object, follower])

run = True
while run:
    time = clock.tick(60)
    for events in pygame.event.get():
        if events.type == pygame.QUIT:
            run = False

    keys = pygame.key.get_pressed()
    object.move_x = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
    follower.follow = object
    all_sprites.update(time, window.get_rect())

    window.fill((32, 64, 224))
    pygame.draw.rect(window, (80, 64, 64), (0, ground_level, window.get_width(), window.get_height()-ground_level))
    all_sprites.draw(window)
    pygame.display.flip()

pygame.quit()
exit()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rabbid76