So, i have a moving background for my pygame that is working fine. Now i wanted to add an obstacle (like a rock), that would be at the bottom of the screen and would move with the background. However, the image (obstacle) just disapears after appearing for a couple of seconds. I want the rock to appear over and over, however, it does not appear. Cant figure out whats wrong. Plz help, thanks!
background = pygame.image.load('background.png')
backgroundX = 0
backgroundX2 = background.get_width()
obstacle = pygame.image.load('obstacle.png')
obstacleX = 0
obstacleX2 = obstacle.get_width()
# use procedure for game window rather than using it within loop
def redrawGameWindow():
# background images for right to left moving screen
screen.blit(background, (backgroundX, 0))
screen.blit(background, (backgroundX2, 0))
man.draw(screen)
screen.blit(obstacle, (obstacleX, 380))
screen.blit(obstacle, (obstacleX2, 380))
pygame.display.flip()
pygame.display.update()
The main loop:
while run:
screen.fill(white)
clock.tick(30)
pygame.display.update()
redrawGameWindow() # call procedure
obstacleX -= 1.4
obstacleX2 -= 1.4
if obstacleX < obstacle.get_width() * -10:
obstacleX = obstacle.get_width
if obstacleX2 < obstacle.get_width() * -10:
obstacleX2 = obstacle.get_width()
The surface.blit() (i.e.: screen.blit) function takes an image and top-left co-ordinates of where to draw it.
In the code provided the two copies of obstacle are drawn at obstacleX and obstacleX2 - one of which is set to 0, and the other the width of the image. So this should result in the two images being drawn next to each other, on the left side of the window, at line 380.
If these images are no longer being drawn after a while, this could be caused by -
The variables obstacleX and obstacleX2 being changed to an off-screen position
The image obstacle being changed to a blank (or otherwise invisible) version
There is no evidence in the small code sample above, but since the question suggests the images move, it's my guess that the obstacleX and obstacleX2 co-ordinates of the drawing positions are being changed to become off-screen.
EDIT:
It's apparent that your objects are starting at position 0 (window left), and the position is being updated obstacleX -= 1.4 which is moving the obstacle further left. This is why they start on-screen, but soon disappear.
Put your screen dimensions into constants, e.g.:
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
And use these instead of peppering your code with numbers. This reduces the number of changes needed if you decide to change window sizes, and it also allows calculations based on window width.
So start your obstacle just off the screen.
obstacleX = WINDOW_WIDTH # off-screen
obstacleX2 = WINDOW_WIDTH + 100 # Further away from first obstacle
In the main update-loop, when the position of the items change, check to see if they need to be re-cycled back to in-front of the player:
# Move the obstacles 1-pixel to the left
obstacleX -= 1.4
obstacleX2 -= 1.4 # probably just 1 would be better
# has the obstacle gone off-screen (to the left)
if ( obstacleX < 0 - obstacle.get_width() ):
# move it back to the right (off-screen)
obstacleX = WINDOW_WIDTH + random.randint( 10, 100 )
# TODO - handle obstacleX2 similarly
Related
I want to grab the screen width of the x position. So, when I change the screen size I can have it where it already is configuration to the screen size and the player doesn't run off the screen. If that makes any sense.
windows = pygame.display.set_mode((1200,1000))
if player_x > pygame.display.get_surface().x-15:
player_x = pygame.display.get_surface().x-15
This isn't right because it crashes the game now.
Make Player disappear and appear in Pacman-Style
gameDisplay = pygame.display.set_mode((800,600))
x=gameDisplay.get_width()
y=gameDisplay.get_height()
if player.left > x-15:
player.left = 0
Maybe not the explicit solution, but you should be able to build from it, exactly what you want ;)
Im trying to make a rectangle bounce, without going off limits.
I want my rectangle to bounce depending on the wall it touched.
In this code im trying to bounce the rectangle in a 90º angle, but it isn't working.
Im using this to calculate each advance:
rect_x += rectxSpeed
rect_y += rectySpeed
When it reachs the limit
if rect_y>450 or rect_y<0:
rectySpeed=5
rect_y=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=5
rectx_y=rectxSpeed*-(math.pi/2)
Whole code here:
import pygame
import random
import math
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
rect_x= 50.0
rect_y = 50.0
rectxSpeed=5
rectySpeed=5
pygame.init()
# Set the width and height of the screen [width, height]
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Game logic should go here
# --- Screen-clearing code goes here
# Here, we clear the screen to white. Don't put other drawing commands
# above this, or they will be erased with this command.
# If you want a background image, replace this clear with blit'ing the
# background image.
screen.fill(BLACK)
string=str(rect_x)
string2=str(rect_y)
string3="["+string+"]"+"["+string2+"]"
font = pygame.font.SysFont('Calibri', 25, True, False)
text = font.render(string3,True,RED)
screen.blit(text, [0, 0])
#Main rectangle
pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50])
#Second rectangle inside the rectangle 1
pygame.draw.rect(screen, RED, [rect_x+10, rect_y+10, 30, 30])
rect_x += rectxSpeed
rect_y+=rectySpeed
if rect_y>450 or rect_y<0:
rectySpeed=5
rect_y=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=5
rect_x=rectxSpeed*-(math.pi/2)
# --- Drawing code should go here
# --- Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(20)
# Close the window and quit.
pygame.quit()
¿How can i adjust the advance?
This code produce this:
By Changing the reach limit code with:
if rect_y>450 or rect_y<0:
rectySpeed=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=rectxSpeed*-(math.pi/2)
Produces this:
I think it's important to recognize here that the speed of the rectangle object here is a scalar value, not it's vector counterpart velocity. You are attempting to multiply the rectySpeed (a scalar value) by -(math/pi)/2, which is a value that will be returned in radians as #0x5453 mentioned. As far as the rectangle bouncing differently depending on the wall that it contacted, you have not specified the differing constraints that you intend to impose. I think you may want to consider why you want the rectangle to always bounce at a 90° angle. Note that a rectangle that always bounces at a 90° angle would yield some very odd functionality to the player/user.
The angle that the rectangle is approaching the wall measured from the horizontal will be equal to the angle that the rectangle will rebound off the wall measured from the vertical line of the wall to it's new path (in the case of an x-directional bounce).
In terms of an accurate physics engine, you may want to consider just simplifying your mechanics to the following in the case that the rectangle contacts a wall:
if rect_y>450 or rect_y<0:
rectySpeed=rectySpeed*-1
if rect_x>650 or rect_x<0:
rectxSpeed=rectxSpeed*-1
The above ensures that the rectangle object simply changes direction, therefore the magnitude of the rectangle's speed does not change, however the velocity indeed does since it is a vector quantity.
I have an Agar.io like test game where the square player touches the "bit"s and grows. The problem is the bit generation. The bit generates every second and has its own surface. But, the program will not blit the bit (no pun intended) into the initial surface.
Here is the code:
import pygame
import random
pygame.init()
clock = pygame.time.Clock()
display = pygame.display
screen = display.set_mode([640,480])
rect_x = 295
rect_y = 215
display.set_caption("Agar")
d = False
bits = []
size = 50
steps = 0
class Bit:
cscreen = display.set_mode([640,480])
circ = pygame.draw.circle(cscreen, (65, 76, 150), (random.randrange(40, 600), random.randrange(40, 440)), 5)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
d = True
if d == True:
break
# Fill background
screen.fill((5, 58, 0))
# Create player object
player = pygame.draw.rect(screen, (250, 197, 255), [rect_x, rect_y, size, size])
# Moving
if pygame.key.get_pressed()[pygame.K_UP]:
rect_y -= 2
if pygame.key.get_pressed()[pygame.K_DOWN]:
rect_y += 2
if pygame.key.get_pressed()[pygame.K_RIGHT]:
rect_x += 2
if pygame.key.get_pressed()[pygame.K_LEFT]:
rect_x -= 2
# Bit generation
if steps == 60:
new_bit = Bit()
bits.append(new_bit.circ)
screen.blit(new_bit.cscreen, (0,0))
steps = 0
steps += 1
# Collision detection
collision = player.collidelist(bits)
if collision != -1:
bits[collision].cscreen.fill((0,0,0,0))
# Make player larger
size += 10
# FPS limit
clock.tick(60)
# Refresh
display.flip()
P.S. There is no error message.
Thanks in advance.
To have each Bit object have different locations and instance variables, you need to have an __init__ method in your bit class. By adding an __init__, and adding self. to the variables, the blitting works.
But a word of warning. When you make a separate cscreen for each object, and then blit that cscreen to your actual screen, you get rid of every other cscreen thats been blitted, making only 1 Bit viewable at a time. Even if you had cscreen outside of __init__, you would have an issue erasing older ones now, so I advise you take a different approach.
My recommendation is to find a small picture of a dot, and write these lines in the Bit's __init__ method.
self.image = pygame.image.load("reddot.png")
self.rect = self.image.get_rect()
Once you create your bit, add it to a pygame.sprite.Group(). These groups have very handy built in methods. One of them being .draw(). It will go through your Group of Bits and use each's self.image and self.rect to draw them in the correct place every time, without you having to worry about it. When a Bit is eaten, you can delete the Bit from the Group and it is now erased for you.
A random side note: I recommend you change your event loop to just break when it event.type == pygame.QUIT. That way, Python does not need to check the if d == True statement on every frame, while making the code slightly more readable. If it only breaks out of the for loop (for me it exited fine), you can use sys.exit() after importing sys
This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
How to rotate an image(player) to the mouse direction?
(2 answers)
Closed 2 years ago.
Python Version: 3.4
I've recently started using Pygame and I have then started to use the module pyganim to handle animation of sprites.
http://inventwithpython.com/pyganim/
I have run into an issue when I try to rotate the animation. It has a strange effect, such as shown in this example http://i.imgur.com/g6Os9.gif, from this issue:
Weird shifting when using pygame.transform.rotate()
I have tried multiple fixes, such as finding the center of the image etc but as it is a pyganim object, I am blitting to my surface like: (where screen is my surface)
player.blit(screen, playerpos)
Changing the center of it seems to have no effect.
I have also tried swapping the image I'm using for a full black square, and I've still had no luck in debugging it.
I assume it is due to the fact I am blitting the pyganim object to my surface, and that I need to maybe blit it to another surface and rotate that? <- speculation!
The issue I believe lies here,
# Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]),position[0]-(playerpos[0]))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = (playerpos[0]-(player.getRect().width/2), playerpos[1]-(player.getRect().height/2))
player.blit(screen, playerpos1)
As the actual rect of the Pyganim object doesn't change, no matter what rotation is put against it (I have some on screen logging that I verified this with). I think the standard methods for correction are void.
If anyone with some experience using Pyganim could shed some light it would be awesome!
Here is the code in full...
# Import modules
import pygame
from pygame.locals import *
import pyganim
import math
# Initialise
pygame.init()
width, height = 640, 480
screen=pygame.display.set_mode((width, height))
keys = [False, False, False, False]
playerpos=[100,100]
# Load images
player = pyganim.PygAnimation([('resources/images/tdt/p1.png', 0.3), ('resources/images/tdt/p2.png', 0.3), ('resources/images/tdt/p3.png', 0.3), ('resources/images/tdt/p2.png', 0.3)])
player.play()
grass = pygame.image.load("resources/images/tdt/bg.png")
grasswidth = grass.get_width()
grassheight = grass.get_height()
myfont = pygame.font.SysFont("monospace", 20)
# loop
while 1:
# clear the screen before re-drawing
screen.fill(0)
# draw the screen elements
for x in range(width//grasswidth+1):
for y in range(height//grassheight+1):
screen.blit(grass,(x*100,y*100))
# Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]),position[0]-(playerpos[0]))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = (playerpos[0]-(player.getRect().width/2), playerpos[1]-(player.getRect().height/2))
player.blit(screen, playerpos1)
# render text
label = myfont.render("X pos: "+str(playerpos[0])+"| Y pos: "+str(playerpos[1]), 1, (255,255,0))
screen.blit(label, (20, 20))
# Update the screen
pygame.display.flip()
# loop through the events
for event in pygame.event.get():
#check if the event is the X button
if event.type==pygame.QUIT:
##if it is, quit the game
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key==K_w:
keys[0]=True
elif event.key==K_a:
keys[1]=True
elif event.key==K_s:
keys[2]=True
elif event.key==K_d:
keys[3]=True
if event.type == pygame.KEYUP:
if event.key==pygame.K_w:
keys[0]=False
elif event.key==pygame.K_a:
keys[1]=False
elif event.key==pygame.K_s:
keys[2]=False
elif event.key==pygame.K_d:
keys[3]=False
# Move player
if keys[0]: # W
playerpos[1]-=2
elif keys[2]: # S
playerpos[1]+=2
if keys[1]: # A
playerpos[0]-=2
elif keys[3]: # D
playerpos[0]+=2
OK
I have made some progress on this... The code I have written right now is basic and works for the shape in question, as it is easily divisible by 4! :D However, I will re-write it to be able to handle all eventualities, however, it may be that the boxes have to be square: here is the code now:
playerpos1 = rMoveAmount(playerpos,angle, 44)
player.blit(screen, playerpos1)
With rMoveAmount being:
def rMoveAmount(position, degrees, picsize):
#MOVE A BOX DEPENDING ON THE AMOUNT ROTATED
degTest = int(((360-degrees*57.29)-180)%90)
#Has to be split up into two sections, before the half 90 degree rotation
if degTest < 45:
t = int((((360-angle*57.29)-180)%45)//4)
nPos = (position[0] - t , position[1] - t )
#And after
if degTest > 44:
t = int((((360-angle*57.29)-180)%45)//4)
nPos = (position[0] + t - 11, position[1] + t - 11)
return nPos
So
It takes 1/4 of the angle (which would be between 0 - 11)
Then it uses that as the offset (as my pic is 44px and will always need to be shifted by 1/4)
As you can see, the pic size isn't currently used. And I think it was just luck that my picture size was 44 and fit so snugly into the middle of a half 90 degree rotation, and could also be divided by 11. However, I will work on being able to use this with more flexible sizes and update.
Edit 3:
Ok, this one should now work for all square images, it will also center the cursor correctly. I will just show the changes made, not the whole code again:
# 3 - Load images
player = pyganim.PygAnimation([('resources/images/tdt/p1.png', 0.3), ('resources/images/tdt/p2.png', 0.3), ('resources/images/tdt/p3.png', 0.3), ('resources/images/tdt/p2.png', 0.3)])
player.play()
playerSize = player.getRect().width
# 6.1 - Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]+(playerSize/2)),position[0]-(playerpos[0]+(playerSize/2)))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = rMoveAmount(playerpos,angle, playerSize)
player.blit(screen, playerpos1)
And the method rMoveAmount changes:
def rMoveAmount(position, degrees, picsize):
#MOVE A BOX DEPENDING ON THE AMOUNT ROTATED
degTest = int(((360-degrees*57.29)-180)%90)
quartsize = picsize / 4
#Has to be split up into two sections, before the half 90 degree rotation
if degTest < 45:
#t = int((((360-angle*57.29)-180)%45)//4)
jump = (quartsize / 44)
nPos = (position[0] - (jump * degTest) , position[1] - (jump * degTest) )
#And after
if degTest > 44:
jump = (quartsize / 44)
nPos = (position[0] + (jump * degTest) - (picsize/2) , position[1] + (jump * degTest) - (picsize/2))
return nPos
Will now work with any square images.
I would like to position my image and rect, but they are already moving back and forth. Can I still manually position their start point at this stage, when they are already on the move?
Currently the Image bounces back and forth on the screen. I would like to position the start point of this motion to an arbitrary point, say (0,100) of my window. Is there a way to set this positioning of my image and rect so that it can keep it's movement but just start from another point? Currently it's stuck on the top, I've tried a few things that didn't work:
self.image = pygame.image.load('picture.jpg')
self.rect = self.image.get_rect()
self.screen.fill(self.bg_color)
self.rect.move_ip(5,0) #only moving on the x axis
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
self.screen.blit(self.image,self.rect)
pygame.display.flip()
Is there a way where I can set a starting position for my Surface object and rectangle?
Sure. Just do
self.rect.move_ip(0, 100)
to move to (0,100).
See the pygame.Rect docs for more.