I am trying to make the arc follow my mouse while staying on the circle path.
I do not know why it is not working.
When I run it, it just create a pygame white screen with no error.
Here is my code:
from __future__ import division
from math import atan2, degrees, pi
import math
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption("globe")
CENTER = (400, 400)
RADIUS = 350
running = True
def drawCircleArc(screen,color,center,radius,startDeg,endDeg,thickness):
(x,y) = center
rect = (x-radius,y-radius,radius*2,radius*2)
startRad = math.radians(startDeg)
endRad = math.radians(endDeg)
pygame.draw.arc(screen,color,rect,startRad,endRad,thickness)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
mouse = pygame.mouse.get_pos()
relx = mouse[0] - CENTER[0]
rely = mouse[1] - CENTER[1]
rad = atan2(-rely,relx)
rad %= 2*pi
degs = degrees(rad)
screen.fill((152,206,231))
drawCircleArc(screen,(243,79,79),CENTER,RADIUS, degs + 90,degs + 100 ,10)
pygame.draw.circle(screen, (71,153,192), CENTER, RADIUS)
pygame.display.update()
pygame.quit()
Picture
Picture2
What I really want is the following picture
Thank you
I think the follow will do what you want. I fixed two problems:
You were drawing the graphics in the wrong order and covering up the short reddish arc (they need to be drawn from back to front), and
The two literal values you were adding to the calcuated degs angle were too large.
I also made a several other changes that weren't strictly needed, including reformatting the code follow the PEP 8 - Style Guide for Python Code guidelines and adding a pygame.time.Clock to slow down the refresh rate to something I though was more reasonable.
from __future__ import division
from math import atan2, degrees, pi
import math
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption("globe")
clock = pygame.time.Clock()
FPS = 60 # Frames per sec
CENTER = (400, 400)
RADIUS = 350
running = True
def draw_circle_arc(screen, color, center, radius, start_deg, end_deg, thickness):
x, y = center
rect = (x-radius, y-radius, radius*2, radius*2)
start_rad = math.radians(start_deg)
end_rad = math.radians(end_deg)
pygame.draw.arc(screen, color, rect, start_rad, end_rad, thickness)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
mouse = pygame.mouse.get_pos()
relx = mouse[0] - CENTER[0]
rely = mouse[1] - CENTER[1]
rad = atan2(-rely, relx)
degs = degrees(rad)
screen.fill((152,206,231))
pygame.draw.circle(screen, (71,153,192), CENTER, RADIUS)
draw_circle_arc(screen, (243,79,79), CENTER, RADIUS, degs-10, degs+10, 10)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
Here's what it looks like running
Related
I'm trying to make a white rectangle rotate like a the hands of a clock in pygame using this code,
import random, pygame, math, sys
from pygame.locals import *
Blue = (0,0,255)
Black = (0, 0, 0)
Green = (0,255,0)
White = (255,255,255)
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
FPS = 30
fpsClock = pygame.time.Clock()
Sail = pygame.Surface([100,10])
Sail.set_colorkey (Black)
Sail.fill(White)
degrees = 0
hyp = 100
x = 200
y = 150
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
adj = 10 * math.cos(degrees)
opp = 10 * math.sin(degrees)
dx = adj + 200
dy = opp + 150
rotatedSail = pygame.transform.rotate(Sail, degrees)
Sail_rect = Sail.get_rect(topleft = (dx, dy))
DISPLAYSURF.fill(Blue)
DISPLAYSURF.blit(rotatedSail, Sail_rect)
pygame.display.flip()
fpsClock.tick(FPS)
degrees += 1
but the rectangle rotates in a weird way. I would appreciate it if you could keep the suggestion as simple and as close to my code as possible, because I'm just starting to learn. Plus i know it's easier to do it using an image of a rectangle, but I'm trying to use a surface.
can anyone help?
You need to get the bounding rectangle of the rotated rectangle and set the center of the rectangle by (x, y) (see also How do I rotate an image around its center using PyGame?):
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
rotatedSail = pygame.transform.rotate(Sail, degrees)
rotatedSail_rect = rotatedSail.get_rect(center = (x, y))
DISPLAYSURF.fill(Blue)
DISPLAYSURF.blit(rotatedSail, rotatedSail_rect)
pygame.display.flip()
fpsClock.tick(FPS)
degrees += 1
To rotate the object around another point than the center point is much more complicate. A general solution is described in the answer to How can you rotate an image around an off center pivot in PyGame.
Complete Example:
import pygame, math, sys
from pygame.locals import *
Blue = (0,0,255)
Black = (0, 0, 0)
Green = (0,255,0)
White = (255,255,255)
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
FPS = 30
fpsClock = pygame.time.Clock()
Sail = pygame.Surface([100,10])
Sail.set_colorkey (Black)
Sail.fill(White)
degrees = 0
hyp = 100
x = 200
y = 150
def blitRotate(surf, image, pos, originPos, angle):
# calcaulate the axis aligned bounding box of the rotated image
w, h = image.get_size()
sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle))
min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
# calculate the translation of the pivot
pivot = pygame.math.Vector2(originPos[0], -originPos[1])
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
# calculate the upper left origin of the rotated image
origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
# get a rotated image
rotated_image = pygame.transform.rotate(image, angle)
# rotate and blit the image
surf.blit(rotated_image, origin)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.fill(Blue)
blitRotate(DISPLAYSURF, Sail, (x, y), (0, 5), degrees)
pygame.display.flip()
fpsClock.tick(FPS)
degrees += 1
I want to move the image of a Rect object, is this possible?
examples:
1 - make a waterfall with the water animated (make the water image scroll)
2 - adjust location of the image not the rect
note: these are just examples not the code I am working on
You can shift the surface image in place with pygame.Surface.scroll. For instance, call
water_surf.scroll(0, 1)
However, this will not satisfy you. See pygame.Surface.scroll:
Move the image by dx pixels right and dy pixels down. dx and dy may be negative for left and up scrolls respectively. Areas of the surface that are not overwritten retain their original pixel values.
You may want to write a function that overwrites the areas wich are not overwritten, with the pixel that is scrolled out of the surface:
def scroll_y(surf, dy):
scroll_surf = surf.copy()
scroll_surf.scroll(0, dy)
if dy > 0:
scroll_surf.blit(surf, (0, dy-surf.get_height()))
else:
scroll_surf.blit(surf, (0, surf.get_height()+dy))
return scroll_surf
once per frame to create a water flow effect like a waterfall.
To center an image in a rectangular area, you need to get the bounding rectangle of the image and set the center of the rectnagle through the center of the area. Use the rectangle to blit the image:
area_rect = pygame.Rect(x, y, w, h)
image_rect = surf.get_rect()
image_rect.center = area_rect.center
screen.blit(surf, image_rect)
The same in one line:
screen.blit(surf, surf.get_rect(center = area_rect.center))
Minimal example:
repl.it/#Rabbid76/PyGame-SCroll
import pygame
def scroll_y(surf, dy):
scroll_surf = surf.copy()
scroll_surf.scroll(0, dy)
if dy > 0:
scroll_surf.blit(surf, (0, dy-surf.get_height()))
else:
scroll_surf.blit(surf, (0, surf.get_height()+dy))
return scroll_surf
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
rain_surf = pygame.image.load('rain.png')
dy = 0
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window_center = window.get_rect().center
scroll_surf = scroll_y(rain_surf, dy)
dy = (dy + 1) % rain_surf.get_height()
window.fill(0)
window.blit(scroll_surf, scroll_surf.get_rect(center = window_center))
pygame.display.flip()
pygame.quit()
exit()
I have a display area and a surface that is blitted on the display. On the surface is an image, in this case a rect. In the future it may be multiple rects or lines drawn on the surface keep that in mind.
I am trying to enlarge (by pressing x) the Rect named Sprite that is on SpriteSurface and SpriteSurface as well as the whole display window. The SpriteSurface image should be centered despite the resize. Currently the window will enlarge and the image stays centered, but if you uncomment the spritesizeX and Y lines the image gets larger but too big too fast and the window doesn't seem to enlarge big enough. Lowering the values shows that the offset of centering gets thrown off after the first resize. I feel like the solution should be relatively easy but im stumped. Any help would be appreciated.
Settings.py
spriteSizeX = 30
spriteSizeY = 30
SpHalfX = int(round(spriteSizeX / 2))
SpHalfY = int(round(spriteSizeY / 2))
multiplyer = 3
windowSizeX = int(round(spriteSizeX * multiplyer))
windowSizeY = int(round(spriteSizeY * multiplyer))
HalfWinX = int(round((windowSizeX / 2) - SpHalfX))
HalfWinY = int(round((windowSizeY / 2) - SpHalfY))
Orange = (238,154,0)
Gold = (255,215,0)
Black = (0,0,0)
Blue = (0,0,255)
Gray = (128,128,128)
DarkGray = (100,100,100)
Green = (0,128,0)
Lime = (0,255,0)
Purple = (128,0,128)
Red = (255,0,0)
Teal = (0,200, 128)
Yellow = (255,255,0)
White = (255,255,255)
run = True
SpriteCapture.py
#!/usr/local/bin/python3.6
import sys, pygame
from pygame.locals import *
from settings import *
pygame.init()
pygame.display.set_caption("Sprite Capture")
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
while run == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if pygame.key.get_pressed()[pygame.K_s]:
pygame.image.save(SpriteSurface, 'img1.png')
run = False
if pygame.key.get_pressed()[pygame.K_q]:
run = False
if pygame.key.get_pressed()[pygame.K_z]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += -10
windowSizeY += -10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
if pygame.key.get_pressed()[pygame.K_x]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += 10
windowSizeY += 10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
Screen.fill(Black)
pygame.draw.rect(SpriteSurface,Orange,Sprite)
Screen.blit(SpriteSurface, (HalfWinX,HalfWinY))
pygame.display.flip()
If you want to scale your surfaces or rects according to the screen size, you can define a zoom_factor variable which you can just increase when a key gets pressed and then use it to scale the window and the surfaces. Multiply it by the original screen width and height to scale the window, and also scale your surfaces with pygame.transform.rotozoom and pass the zoom_factor as the scale argument.
import sys
import pygame
from pygame.locals import *
width = 30
height = 30
multiplyer = 3
window_width = round(width * multiplyer)
window_height = round(height * multiplyer)
zoom_factor = 1
ORANGE = (238,154,0)
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode((window_width, window_height), RESIZABLE)
screen_rect = screen.get_rect() # A rect with the size of the screen.
clock = pygame.time.Clock()
# Keep a reference to the original image to preserve the quality.
ORIG_SURFACE = pygame.Surface((width, height))
ORIG_SURFACE.fill(ORANGE)
surface = ORIG_SURFACE
# Center the rect on the screen's center.
rect = surface.get_rect(center=screen_rect.center)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
run = False
elif event.key == pygame.K_z:
zoom_factor = round(zoom_factor-.1, 1)
# Scale the screen.
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect() # Get a new rect.
# Scale the ORIG_SURFACE (the original won't be modified).
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center) # Get a new rect.
elif event.key == pygame.K_x:
zoom_factor = round(zoom_factor+.1, 1)
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect()
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center)
# Note that the rect.w/screen_rect.w ratio is not perfectly constant.
print(zoom_factor, screen_rect.w, rect.w, rect.w/screen_rect.w)
screen.fill(BLACK)
screen.blit(surface, rect) # Blit the surface at the rect.topleft coords.
pygame.display.flip()
clock.tick(60)
Alternatively, you could just blit all of your surfaces onto a background surface, then scale this background with pygame.transform.rotozoom each frame and blit it onto the screen. However, scaling a big background surface each frame will be bad for the performance.
I am currently trying to digitalize an boardgame I invented (repo: https://github.com/zutn/King_of_the_Hill). To make it work I need to check if one of the tiles (the arcs) on this board have been clicked. So far I have not been able to figure a way without giving up the pygame.arc function for drawing. If I use the x,y position of the position clicked, I can't figure a way out to determine the exact outline of the arc to compare to. I thought about using a color check, but this would only tell me if any of the tiles have been clicked. So is there a convenient way to test if an arc has been clicked in pygame or do I have to use sprites or something completely different? Additionally in a later step units will be included, that are located on the tiles. This would make the solution with the angle calculation postet below much more diffcult.
This is a simple arc class that will detect if a point is contained in the arc, but it will only work with circular arcs.
import pygame
from pygame.locals import *
import sys
from math import atan2, pi
class CircularArc:
def __init__(self, color, center, radius, start_angle, stop_angle, width=1):
self.color = color
self.x = center[0] # center x position
self.y = center[1] # center y position
self.rect = [self.x - radius, self.y - radius, radius*2, radius*2]
self.radius = radius
self.start_angle = start_angle
self.stop_angle = stop_angle
self.width = width
def draw(self, canvas):
pygame.draw.arc(canvas, self.color, self.rect, self.start_angle, self.stop_angle, self.width)
def contains(self, x, y):
dx = x - self.x # x distance
dy = y - self.y # y distance
greater_than_outside_radius = dx*dx + dy*dy >= self.radius*self.radius
less_than_inside_radius = dx*dx + dy*dy <= (self.radius- self.width)*(self.radius- self.width)
# Quickly check if the distance is within the right range
if greater_than_outside_radius or less_than_inside_radius:
return False
rads = atan2(-dy, dx) # Grab the angle
# convert the angle to match up with pygame format. Negative angles don't work with pygame.draw.arc
if rads < 0:
rads = 2 * pi + rads
# Check if the angle is within the arc start and stop angles
return self.start_angle <= rads <= self.stop_angle
Here's some example usage of the class. Using it requires a center point and radius instead of a rectangle for creating the arc.
pygame.init()
black = ( 0, 0, 0)
width = 800
height = 800
screen = pygame.display.set_mode((width, height))
distance = 100
tile_num = 4
ring_width = 20
arc = CircularArc((255, 255, 255), [width/2, height/2], 100, tile_num*(2*pi/7), (tile_num*(2*pi/7))+2*pi/7, int(ring_width*0.5))
while True:
fill_color = black
for event in pygame.event.get():
# quit if the quit button was pressed
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
x, y = pygame.mouse.get_pos()
# Change color when the mouse touches
if arc.contains(x, y):
fill_color = (200, 0, 0)
screen.fill(fill_color)
arc.draw(screen)
# screen.blit(debug, (0, 0))
pygame.display.update()
Recently began a new pygame project, asteroids. I've been working on getting an image to rotate in the direction of the cursor, which i'm finding irritatingly hard. Any help is appreciated, here is the code for the rotation so far:
import sys, pygame, math, time;
from pygame.locals import *;
spaceship = ('spaceship.png')
mouse_c = ('crosshair.png')
backg = ('background.jpg')
fire_beam = ('beam.png')
pygame.init()
screen = pygame.display.set_mode((800, 600))
bk = pygame.image.load(backg).convert_alpha()
mousec = pygame.image.load(mouse_c).convert_alpha()
space_ship = pygame.image.load(spaceship).convert_alpha()
f_beam = pygame.image.load(fire_beam).convert_alpha()
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
x, y = 357, 300 #position of space_ship, (line 38 btw, second from bottom)
while True:
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
print("Left Button Pressed")
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
print("Right Button Pressed")
if event.type == MOUSEMOTION:
clock.tick(60)
x1, y1 = pygame.mouse.get_pos()
x2, y2 = x, y
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
pygame.transform.rotate(space_ship, (degs))
print degs #Prints correct output..
pygame.display.update() #the image flickers, but does not rotate
pos = pygame.mouse.get_pos()
screen.blit(mousec, (pos))
screen.blit(space_ship, (375, 300))
pygame.display.update()
From the documentation: "A Surface transform is an operation that moves or resizes the pixels. All these functions take a Surface to operate on and return a new Surface with the results."
As it is, you operate the transform, but throw away the result.
You could just change the line
pygame.transform.rotate(space_ship, (degs))
to:
space_ship = pygame.transform.rotate(space_ship, (degs))
In order to see it working, but it would not be a good :
you'd need to re-calculate your degrees to have only the difference
in degrees from one iteration to the next - but worse, successive raster-picture
transforms would degenerate your spaceship into an amorphous blob of pixels very fast.
The right thing to do is to keep a reference to your original spacehsip pictures, and always rotate that instead.
So, before your main loop, you do:
space_ship_image = pygame.image.load(spaceship).convert_alpha()
And inside the loop, the aforementioned:
space_ship = pygame.transform.rotate(space_ship_image, (degs))
BTW, raster artifacts being what they are, yo probably will want to use a larger
version of your ship in your "spaceship.png" file, and use "rotozoom" instead of "rotate",
scaling down as you rotate the ship to get your final image.