Pygame rotation trouble - python

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.

Related

Scaling up window in pygame causing lag [duplicate]

This question already has an answer here:
Set the width and height of a pygame surface
(1 answer)
Closed 2 years ago.
Recently I've been trying to build a game in pygame in a low res, pixel art style.
In order to make my game usable I have to scale up my window, so here's a basic example of the code I've developed to do that, where SCALE in the value by which the whole window is scaled up, and temp_surf is the surface I blit my graphics onto before the scale function scales them up.
import sys
import ctypes
import numpy as np
ctypes.windll.user32.SetProcessDPIAware()
FPS = 60
WIDTH = 150
HEIGHT = 50
SCALE = 2
pg.init()
screen = pg.display.set_mode((WIDTH*SCALE, HEIGHT*SCALE))
pg.display.set_caption("Example resizable window")
clock = pg.time.Clock()
pg.key.set_repeat(500, 100)
temp_surf = pg.Surface((WIDTH, HEIGHT))
def scale(temp_surf):
scaled_surf = pg.Surface((WIDTH*SCALE, HEIGHT*SCALE))
px = pg.surfarray.pixels2d(temp_surf)
scaled_array = []
for x in range(len(px)):
for i in range(SCALE):
tempar = []
for y in range(len(px[x])):
for i in range(SCALE):
tempar.append(px[x, y])
scaled_array.append(tempar)
scaled_array = np.array(scaled_array)
pg.surfarray.blit_array(scaled_surf, scaled_array)
return scaled_surf
while True:
clock.tick(FPS)
#events
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
pg.quit()
sys.exit()
#update
screen.fill((0,0,0))
temp_surf.fill ((255,255,255))
pg.draw.rect(temp_surf, (0,0,0), (0,0,10,20), 3)
pg.draw.rect(temp_surf, (255,0,0), (30,20,10,20), 4)
scaled_surf = scale(temp_surf)
#draw
pg.display.set_caption("{:.2f}".format(clock.get_fps()))
screen.blit(scaled_surf, (0,0))
pg.display.update()
pg.display.flip()
pg.quit()
For this example, there is very little lag. However when I try to implement this code in my game, the fps drops from 60 to more like 10.
Is there a more efficient way of scaling up a pygame window that I don't know about? Would there be a way for my code to run more efficiently? I'm open to any suggestions.
Do not recreate scaled_surf in every frame. Creating a pygame.Surface my be an time consuming operation. Create scaled_surf once and continuously use it.
Furthermore I recommend to use pygame.transform.scale() or pygame.transform.smoothscale(), which are designed for this task:
scaled_surf = pg.Surface((WIDTH*SCALE, HEIGHT*SCALE))
def scale(temp_surf):
pg.transform.scale(temp_surf, (WIDTH*SCALE, HEIGHT*SCALE), scaled_surf)
return scaled_surf

An arc that follows my mouse but stays on the circle

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

Pygame Rectangle and Mouse pos Collidepoint not working

I'm making a basic game where I have a surface and everytime I click on the surface it moves 5 pixels to the right. The program is working just fine without the checkCollide(event) function, but when I put the that condition it doesn't move. What is wrong?
My code until now is this
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300,300))
def checkCollide(event):
k = 0
a,b = event.pos
x = P1[0].get_rect()
if x.collidepoint(a,b):
return True
return False
CP1 = [(150, 150)
,(155, 150)
,(160, 150)
,(165, 150)
,(170, 150)
,(175, 150)
,(180, 150)
,(185, 150)
,(190, 150)]
statp1_1 = 0
WHITE = (255,255,255)
DISPLAYSURF.fill(WHITE)
while True: # the main game loop
P1 = [pygame.image.load('PAzul.png'),CP1[statp1_1],statp1_1]
DISPLAYSURF.blit(P1[0], P1[1])
e = pygame.event.get()
for event in e:
if event.type == MOUSEBUTTONUP:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
statp1_1 +=1
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Thank you
Check your logic in these lines of your function:
x = P1[0][0].get_rect()
if x.collidepoint(a,b):
return True
return False
Your code hinges on this bit:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
So you're never evaluating this piece to be true.
I just realized what was wrong. When I do x = P1[0].get_rect() it creates a surface with topleft at (0,0).
What I needed to do was change the position of the rectangle using x.topleft = P1[1]
I've got some tips for you. First store the rect in the P1 list (it contains only the image and the rect in the following example, but maybe you could also add the statp1_1 index to it). Now we can just move this rect, if the user clicks on it (in the example I set the topleft attribute to the next point). Read the comments for some more tips. One thing you need to fix is to prevent the game from crashing when the statp1_1 index gets too big.
import sys
import pygame
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300, 300))
WHITE = (255, 255, 255)
# Don't load images in your while loop, otherwise they have to
# be loaded again and again from your hard drive.
# Also, convert loaded images to improve the performance.
P1_IMAGE = pygame.image.load('PAzul.png').convert() # or .convert_alpha()
# Look up `list comprehension` if you don't know what this is.
CP1 = [(150+x, 150) for x in range(0, 41, 5)]
statp1_1 = 0
# Now P1 just contains the image and the rect which stores the position.
P1 = [P1_IMAGE, P1_IMAGE.get_rect(topleft=CP1[statp1_1])]
clock = pygame.time.Clock() # Use this clock to limit the frame rate.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
if P1[1].collidepoint(event.pos):
print('clicked')
statp1_1 += 1
# Set the rect.topleft attribute to CP1[statp1_1].
P1[1].topleft = CP1[statp1_1]
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(P1[0], P1[1]) # Blit image at rect.topleft.
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.

I get an incorrect position from .get_rect()?

I'm currently working on a school project where I'm making a "hexcells" similar game in pygame and now I'm trying to blit an a new image if the user has clicked a current image. It will blit an image in the top left area, if clicked in the top left area, but not if I click any of the existing images. I told the program to print the coordinates from the images with help of the .get_rect() function, but it remains the same whereever I click and the coordinates aren't even where a image is. Can someone help me understand how this works and help me blit the new images on top of the existing images? Code below is not the entire document, however there is so much garbage/trash/unused code so I'd thought I spare you the time of looking at irrelevant code. Also sorry if the formatting is wrong or the information isn't enough, I tried my best.
import pygame, sys
from pygame.locals import *
#Magic numbers
fps = 30
winW = 640
winH = 480
boxSize = 40
gapSize = 75
boardW = 3
boardH = 3
xMargin = int((winW - (boardW * (boxSize + gapSize))) / 2)
yMargin = int((winW - (boardW * (boxSize + gapSize))) / 2)
#Lil bit o' color R G B
NAVYBLUE = ( 60, 60, 100)
correctCords = [[175,275,375],[375,275,175]]
bgColor = NAVYBLUE
unC = pygame.image.load("unC.png")
cor = pygame.image.load("correct.png")
inc = pygame.image.load("wrong.png")
correct = "Correct"
inCorrect = "Incorrect"
def main():
global FPSCLOCK, DISPLAYSURF
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((winW, winH))
mousex = 0 #stores x-coordinate of mouse event
mousey = 0 #stores y-coordinate of mouse event
pygame.display.set_caption("Branches")
DISPLAYSURF.fill(bgColor)
gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize)
while True:
mouseClicked = False
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEMOTION:
mousex,mousey = event.pos
elif event.type == MOUSEBUTTONUP:
mousex, mousey = event.pos
pos = pygame.mouse.get_pos()
mouseClicked = True
unCa = unC.get_rect()
corA = cor.get_rect()
print unCa
print corA
print pos
if unCa.collidepoint(pos):
DISPLAYSURF.blit(cor,(mousey,mousex))
"""lada = unC.get_rect()
lada =
if mousex and mousey == lada:
for x in correctCords:
for y in x:
for z in x:
if mousey and mousex == z and y:
DISPLAYSURF.blit(cor,(mousey,mousex))
print lada"""
pygame.display.update()
FPSCLOCK.tick(fps)
def gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize):
grid = []
cordX = []
cordY = []
correctRecs = []
#cordinates = []
#cordinates.append([])
#cordinates.append([])
#cordinates.append([])
#this is basically getBoard() all over again
#This part will arrange the actual backend grid
for row in range(3):
grid.append([])
#cordinates[0].append(gapSize+(row+1)*100)
#cordinates[1].append(gapSize+(row+1)*100)
#cordinates[2].append(gapSize+(row+1)*100)
for column in range(3):
grid[row].append(inCorrect)
for row in range(3):
cordX.append([])
for column in range(3):
cordX[row].append(gapSize+(row+1)*100)
for row in range(3):
cordY.append([])
for column in range(3):
cordY[row].append(gapSize+(column+1)*100)
#print cordX[0][0], cordY[0][0]
grid[0][2] = correct
grid[1][1] = correct
grid[2][0] = correct
#Y-AXEL SKRIVS FoRST ([Y][X])
#print cordinates[2][1]
DISPLAYSURF.blit(cor,(100,100))
#Let's draw it as well
for row in range(3):
for column in range(3):
DISPLAYSURF.blit(unC,(gapSize+(row+1)*100,gapSize+(column+1)*100))
main()
Also real sorry about the horrible variable naming and occasional swedish comments.
unCa = unC.get_rect() gives you only image size - so use it only once at start (before while True) - and later use the same unCa all the time to keep image position and change it.
btw: better use more readable names - like unC_rect
ie.
# move 10 pixel to the right
unC_rect.x += 10
# set new position
unC_rect.x = 10
unC_rect.right = 100
unC_rect.topleft = (10, 200)
unC_rect.center = (10, 200)
# center on screen
unC_rect.center = DISPLAYSURF.get_rect().center
etc.
And then use this rect to blit image
blit(unC, unC_rect)
and check collision with other rect
if unC_rect.colliderect(other_rect):
or with point - like mouse position
elif event.type == MOUSEMOTION:
if unC_rect.collidepoint(pygame.mouse.get_pos()):
hover = True
# shorter
elif event.type == MOUSEMOTION:
hover = unC_rect.collidepoint(pygame.mouse.get_pos()):

Different colors for shapes through iterations in Python / Pygame?

I'm new to stackoverflow, but was hoping for a little insight from more advanced programmers. I am switching majors to Computer Science next semester and am taking an intro class learning some beginner's Python programming. I have already finished the program below (the assignment was to make a program that draws ovals on the window surface by filling in some of the professor's code, not too bad at all) but I wanted to add a little something extra: As you can see, I have the color of the ovals set to be random, but it stays the same until the program is restarted entirely i.e. all of the ovals are that particular color for the length of the program. With the code written the way it is, I can't figure out a way to get the color to change for each oval. Keep in mind, this is all for kicks, but if anyone's feeling especially helpful or creative, I'm curious to see what you have to say. Let me know if I can expound on anything. Thanks!
import pygame, random, sys
WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")
#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)
while not finished:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
event.key == pygame.K_ESCAPE):
finished = True
elif event.type == pygame.KEYDOWN:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
pressed[pygame.K_RALT]):
finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
for oval in ovals:
completedOvals.append (oval)
if draw == True:
del ovals [:]
#The above function ensures only one oval is onscreen at any given time
endXY = event.pos
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
ovals.append (pygame.Rect (left, top, width, height))
windowSurface.fill(BACKGROUNDCOLOR)
for oval in ovals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)
for completedOval in completedOvals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)
pygame.display.update()
pygame.quit()
Your problem is quite simple. You set OVAL_COLOR once. But every time you make reference to the variable OVAL_COLOR, you're not creating a new random color, you're re-using the RGB color that was randomly generated when you created the variable.
Now, the way your program is structured, you maintain a list of all complete ovals that you're re-drawing every time the draw variable is set to true. If you place the OVAL_COLOR variable inside the for loop, you will update the color with every mouse movement, changing the color of the oval being drawn, as well as the color of all the old ovals being re-drawn.
The solution to have a new random oval color is to set the variable OVAL_COLOR when the mouse button goes down. That way, the oval color won't change as you drag the mouse to adjust the oval. But, given the current structure of the program, you'll need to save the oval colors assigned to completed ovals, or you'll still have the oval color change each time.
When the mouse button is pressed down, we want a new random color for our circle. Generate a random value, which will be used every time the circle is re-drawn.
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
draw = True
When the mouse button is released, save the coordinates for the oval, along with the color that it was drawn with.
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
# print len(ovals) # (always ==1)
completedOvals.append ((ovals[-1], OVAL_COLOR))
When we iterate through these completed ovals, draw them with the same color each time.
for (completedOval, color) in completedOvals:
pygame.draw.ellipse(windowSurface, color, completedOval)
Create a simple Oval() class, that contains it's color, and size.
import pygame
from pygame.locals import *
class Oval(object):
"""handle, and draw basic ovals. stores Rect() and Color()"""
def __init__(self, startXY, endXY):
self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
self.rect = Rect(0,0,1,1)
self.coord_to_oval(startXY, endXY)
def draw(self):
pygame.draw.ellipse(windowSurface, self.color, self.rect)
def coord_to_oval(self, startXY, endXY):
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
self.rect = Rect(left, top, width, height)
# main loop
while not finished:
for event in pygame.event.get():
# events, and creation:
# ... your other events here ...
elif event.type == MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type ==MOUSEBUTTONUP:
# on mouseup, create instance.
endXY = event.pos
oval_new = Oval(startXY, endXY)
completedOvals.append(oval_new)
# draw them:
for oval in ovals:
oval.draw()
for oval in completedOvals:
oval.draw()
I mostly left out your non-completed ovals. Was that to show the size before clicking?

Categories

Resources