when the rectangle descends the ramp, the rectangle shakes - python

I don't know if there is a better way to implement ramps.
First i calculate the points that belong to the hipotenuse and use collidepoint to see if there is a collision between the rectangle and any point that belongs to the hipotenuse, then i update the rectangle based on the point where there was a collision.
Being careful when the rectangle is at the top of the ramp.
The rectangle ascends the ramp perfectly, but when the rectangle descends the ramp, the rectangle shakes.
import sys
import pygame
from pygame.locals import *
pygame.init()
fps = 60
fpsClock = pygame.time.Clock()
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
def draw_grid():
for y in range(0,height,32):
pygame.draw.line(screen,'red',(0,y),(width,y))
for x in range(0,width,32):
pygame.draw.line(screen,'red',(x,0),(x,height))
class Ramp(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height), pygame.SRCALPHA)
#self.image.fill('green')
pygame.draw.polygon(self.image, color,
points=[(0, 0), (0, height), (width, height)])
self.rect = self.image.get_rect(topleft=(x, y))
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill('blue')
self.rect = self.image.get_rect(topleft=(x,y))
self.speed = 5
self.direction = pygame.math.Vector2(0,0)
self.gravity = 0.9
self.initial_jump = -20
self.on_ground = True
def apply_gravity(self):
self.direction.y += self.gravity
self.rect.y += self.direction.y
def move(self):
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
self.direction.x = -self.speed
elif keys[K_RIGHT]:
self.direction.x = self.speed
else:
self.direction.x = 0
if keys[K_UP] and self.on_ground:
self.direction.y = self.initial_jump
self.on_ground = False
self.rect.x += self.direction.x
def check_borders(self):
if self.rect.x <= 0:
self.rect.x = 0
if self.rect.right >= width:
self.rect.right = width
if self.rect.bottom >= height:
self.rect.bottom = height
self.direction.y = 0
self.on_ground = True
if self.rect.colliderect(ramp_rect):
if self.direction.x > 0 and abs(self.rect.right-ramp_rect.left) <= 5:
self.rect.right = ramp_rect.left
# ramp stuff
for p in hypotenuse_points:
if self.rect.collidepoint(p):
if self.rect.left >= ramp_rect.left:
self.rect.bottomleft = p
else:
self.rect.bottom = ramp_rect.top
self.on_ground = True
self.direction.y = 0
def update(self):
self.move()
self.apply_gravity()
self.check_borders()
player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect
m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft
hypotenuse_points = []
for x in range(ramp_rect.left,ramp_rect.right):
hypotenuse_points.append((x,m*(x-x1)+y1)) # Point-slope equation
while True:
screen.fill('white')
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
ramp.draw(screen)
player.update()
player.draw(screen)
#draw_grid()
pygame.draw.lines(screen,'black',False,hypotenuse_points,3)
pygame.display.update()
fpsClock.tick(fps)

There is no problem with your code. Only gravity is too weak. The movement is so fast that gravity is acting too late. Note that instead of moving down the slope, you move to the right and then fall.
Of course there is one problem with your code. Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location of the rectangle.
Instead of the list of points I suggest to compute the height of the ramp under the palyer:
if self.rect.colliderect(ramp_rect):
ratio = ramp_rect.height / ramp_rect.width
self.rect.bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
self.y = self.rect.y
Complete example:
import sys
import pygame
from pygame.locals import *
pygame.init()
fps = 60
fpsClock = pygame.time.Clock()
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
def draw_grid():
for y in range(0,height,32):
pygame.draw.line(screen,'red',(0,y),(width,y))
for x in range(0,width,32):
pygame.draw.line(screen,'red',(x,0),(x,height))
class Ramp(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height), pygame.SRCALPHA)
#self.image.fill('green')
pygame.draw.polygon(self.image, color,
points=[(0, 0), (0, height), (width, height)])
self.rect = self.image.get_rect(topleft=(x, y))
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill('blue')
self.rect = self.image.get_rect(topleft=(x,y))
self.x, self.y = self.rect.topleft
self.speed = 5
self.direction = pygame.math.Vector2(0,0)
self.gravity = 0.9
self.initial_jump = -20
self.on_ground = True
def apply_gravity(self):
self.direction.y += self.gravity
self.y += self.direction.y
self.rect.y = round(self.y)
def move(self):
keys = pygame.key.get_pressed()
self.direction.x = (keys[K_RIGHT] - keys[K_LEFT]) * self.speed
if keys[K_UP] and self.on_ground:
self.direction.y = self.initial_jump
self.on_ground = False
self.x += self.direction.x
self.rect.x = round(self.x)
def check_borders(self):
if self.rect.x <= 0:
self.rect.x = 0
self.x = self.rect.x
if self.rect.right >= width:
self.rect.right = width
self.x = self.rect.x
if self.rect.bottom >= height:
self.rect.bottom = height
self.direction.y = 0
self.on_ground = True
self.y = self.rect.y
if self.rect.colliderect(ramp_rect):
if self.old_rect.right-1 <= ramp_rect.left:
self.rect.right = ramp_rect.left
self.x = self.rect.x
else:
ratio = ramp_rect.height / ramp_rect.width
bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
if self.on_ground or self.rect.bottom > bottom:
self.rect.bottom = bottom
self.y = self.rect.y
self.direction.y = 0
self.on_ground = True
def update(self):
self.old_rect = self.rect.copy()
self.move()
self.apply_gravity()
self.check_borders()
player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect
m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft
while True:
screen.fill('white')
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
ramp.draw(screen)
player.update()
player.draw(screen)
pygame.display.update()
fpsClock.tick(fps)

Related

How do I change rect size in pygame with sprites to improve accuracy of turrets? [duplicate]

This question already has answers here:
Can't figure out how to check mask collision between two sprites
(1 answer)
How to get the correct dimensions for a pygame rectangle created from an image
(2 answers)
Closed 3 days ago.
I am trying to make a game in pygame. I want to make a turret that will fire at the player. I have the formula to make this happen, but for some reason, it isn't completely accurate. I am running into the same problem with the player shooting as well. I am trying to use the mouse to aim, and it is off by a little bit.
Here is an image to help with understanding.
The cross is the turret and the ghost is the player. The rest is the bullets that should be hitting the player but it is hitting the rect top left it seems.
It is a bit of a mess but here is the code I am trying to test this with.
import pygame, math, random
pygame.init()
height,width = 1050,1000
clock = pygame.time.Clock()
screen = pygame.display.set_mode((height, width,))
pygame.display.set_caption("Vampires Suck")
pygame.display.flip()
running = True
class Crucifix2(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.sprite = pygame.image.load("image")
self.image = self.sprite
self.rect = self.image.get_rect()
self.rect.x =(450)
self.rect.y = (150)
self.mask = pygame.mask.from_surface(self.image)
def update(self):
pass
class MagicBullet2(pygame.sprite.Sprite):
def __init__(self, speed,x,y, targetx, targety):
super().__init__()
self.sprite = pygame.image.load("Image")
self.image = self.sprite
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
angle = math.atan2(targety - y, targetx - x)
self.dx = math.cos(angle) * speed
self.dy = math.sin(angle) * speed
self.x = x
self.y = y
def update(self):
self.x = self.x + self.dx
self.y = self.y + self.dy
self.rect.x = int(self.x)
self.rect.y = int(self.y)
if self.rect.x < -50 or self.rect.x > width + 50 or self.rect.y < -50 or self.rect.y > height + 50:
self.kill()
def collide(self, spriteGroup):
if pygame.sprite.spritecollide(self, spriteGroup, True, pygame.sprite.collide_mask):
pass
class OurHero(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.sprite = pygame.image.load('image')
self.size = (400,400)
self.current_sprite = 0
self.image =pygame.transform.scale(self.sprite, self.size)
self.rect = self.image.get_rect(topleft = (580, 630))
self.rect.x = (430)
self.rect.y = (380)
self.mask = pygame.mask.from_surface(self.image)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= 3
if keys[pygame.K_RIGHT]:
self.rect.x += 3
if keys[pygame.K_UP]:
self.rect.y -= 3
if keys[pygame.K_DOWN]:
self.rect.y += 3
if self.rect.x > 793:
self.rect.x -= 3
if self.rect.x < 75:
self.rect.x += 3
if self.rect.y < 87:
self.rect.y += 3
if self.rect.y > 789:
self.rect.y -=3
self.current_sprite += 0.027
class aim(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.sprite = pygame.image.load("image")
self.image = self.sprite
self.rect = self.image.get_rect(center = (300,300))
self.rect.inflate(-500,-500)
def update(self):
self.rect.center = pygame.mouse.get_pos()
class MagicBullet(pygame.sprite.Sprite):
def __init__(self, speed,x,y, targetx, targety):
super().__init__()
self.sprite = pygame.image.load("image")
self.image = self.sprite
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
angle = math.atan2(targety - y, targetx - x)
self.dx = math.cos(angle) * speed
self.dy = math.sin(angle) * speed
self.x = x
self.y = y
def update(self):
self.x = self.x + self.dx
self.y = self.y + self.dy
self.rect.x = int(self.x)
self.rect.y = int(self.y)
if self.rect.x < -50 or self.rect.x > width + 50 or self.rect.y < -50 or self.rect.y > height + 50:
self.kill()
mouse_x, mouse_y = pygame.mouse.get_pos()
pygame.mouse.set_visible(False)
mouse = aim()
player = OurHero()
dude_sprite = pygame.sprite.Group()
dude_sprite.add(player)
dude_sprite.add(mouse)
bad = pygame.sprite.Group()
bad3 = pygame.sprite.Group()
bad2 = Crucifix2()
bad.add(bad2)
player_bullets = MagicBullet2(8, bad2.rect.x , bad2.rect.y+90 , player.rect.x, player.rect.y)
bullet_group = pygame.sprite.Group()
bad3.add(player_bullets)
distance_x = player.rect.x - bad2.rect.x
distance_y = player.rect.y - bad2.rect.y
angle = math.atan2(distance_y, distance_x)
dude2 = MagicBullet(5, player.rect.x, player.rect.y, mouse_x, mouse_y)
while running:
mouse_x, mouse_y = pygame.mouse.get_pos()
pygame.display.update()
player_bullets = MagicBullet2(8, bad2.rect.x , bad2.rect.y+90 , player.rect.x + 30, player.rect.y + 30)
bad3.add(player_bullets)
screen.fill('white')
bad3.update()
bad3.draw(screen)
dude_sprite.update()
dude_sprite.draw(screen)
bullet_group.draw(screen)
bad.update()
bad.draw(screen)
bullet_group.update()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
#bullet_group.add(player.creat_bullet())
dude2 = MagicBullet(8, player.rect.x , player.rect.y , mouse_x, mouse_y)
bullet_group.add(dude2)
if event.type == pygame.QUIT:
running = False
clock.tick(120)
I have been searching everywhere for an answer. I am not exactly sure what I am looking for. I am very new to game dev and I am still pretty new with python and pygame. I tried transform.scale() but that seemed to push the character off the screen when I put it into update().

Pygame moving objects randomly

So, I'm making my first game in pygame, and have done OK up to this point. I've been looking at many tutorials but they only show me how to move the object using keys. I just can't make my object move randomly in random directions. Can I please get some help?
import pygame
import random
#create display
screen_h = 500
screen_w = 500
points = 0
bombplacex = random.randint(1,325)
bombplacey = random.randint(1,325)
strawplacex = random.randint(1,325)
strawplacey = random.randint(1,325)
pepperplacex = random.randint(1,325)
pepperplacey = random.randint(1,325)
screen = pygame.display.set_mode((screen_h, screen_w))
pygame.display.set_caption('Button')
# load button image
bomb_img = pygame.image.load('bomb.png').convert_alpha()
straw_img = pygame.image.load('strawberry-png.png').convert_alpha()
pepper_img = pygame.image.load('green pepper.png').convert_alpha()
# button class
class Button():
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(image, (int(width * scale),int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x,y)
self.clicked = False
def draw(self):
action = False
# get mouse position
position = pygame.mouse.get_pos()
# check mouseover and click conditions
if self.rect.collidepoint(position):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
# draw button on screen
screen.blit(self.image, (self.rect.x, self.rect.y))
return action
# create button instances
bomb_button = Button(bombplacex, bombplacey, bomb_img, 0.25)
strawberry_button = Button( strawplacex, strawplacey, straw_img,0.15)
pepper_button = Button(pepperplacex,pepperplacey,pepper_img,0.15)
#game loop
run = True
while run:
screen.fill((153, 50, 204))
# if the bomb is clicked the game will end and if the strawberry is clicked a point will be added.
if bomb_button.draw() == True:
print('GAME OVER')
run = False
elif strawberry_button.draw() == True:
points = points + 1
print('You have',points,'points')
elif pepper_button.draw() == True:
points = points + 1
print('You have',points,'points')
#event handler
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
You're close to implementing your own Sprite class. You should subclass the PyGame sprite and use sprite Groups, it will make your life easier and is worth the investment.
A sprite needs an update() function that is called each game loop iteration so that's where your movement logic would need to be. If wanted to adjust the x and y positions by a random amount you could do something like:
class ShiftyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("blueviolet"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
def update(self):
""" shift randomly - Note doesn't check screen boundaries"""
self.rect.x += random.randint(-5,5)
self.rect.y += random.randint(-5,5)
But perhaps this isn't the random movement you're after.
Here's a block that starts going in a random direction when created and only changes when it bounces off a wall.
class BouncyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("darkgreen"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
# initialise speed on creation
self.speedx = random.randint(-5, 5)
self.speedy = random.randint(-5, 5)
def update(self):
# simplistic bounds checking
width, height = screen.get_size()
if not 0 < self.rect.x < width:
self.speedx *= -1 # reverse direction
self.rect.x += self.speedx
if not 0 < self.rect.y < height:
self.speedy *= -1 # reverse direction
self.rect.y += self.speedy
These sprites will look like:
Full example code:
import pygame
import random
screen = pygame.display.set_mode((500,500))
pygame.init()
sprite_list = pygame.sprite.Group()
class ShiftyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("blueviolet"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
def update(self):
""" shift randomly - Note doesn't check screen boundaries"""
self.rect.x += random.randint(-5,5)
self.rect.y += random.randint(-5,5)
class BouncyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("darkgreen"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
# initialise speed on creation
self.speedx = random.randint(-5, 5)
self.speedy = random.randint(-5, 5)
def update(self):
# simplistic bounds checking
width, height = screen.get_size()
if not 0 < self.rect.x < width:
self.speedx *= -1 # reverse direction
self.rect.x += self.speedx
if not 0 < self.rect.y < height:
self.speedy *= -1 # reverse direction
self.rect.y += self.speedy
for _ in range(5):
block = ShiftyBlock((80,random.randint(40,100)), (random.randint(100,400),random.randint(100,400)))
sprite_list.add(block)
for _ in range(4):
block = BouncyBlock((80,random.randint(40,100)), (random.randint(100,400),random.randint(100,400)))
sprite_list.add(block)
run = True
clock = pygame.time.Clock()
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(pygame.Color("white"))
sprite_list.update()
sprite_list.draw(screen)
pygame.display.update()
clock.tick(60) # limit to 60 FPS
pygame.quit()

pygame collisions while using deltatime

I am trying to understand how to use deltatime in pygame, especially when it comes to collisions. I basically made a square bouncing around a window with some blocks.
Here is the version that doesn't use dt, and that works fine:
import pygame,sys
class Block(pygame.sprite.Sprite):
def __init__(self,pos,size,groups):
super().__init__(groups)
self.image = pygame.Surface(size)
self.image.fill('yellow')
self.rect = self.image.get_rect(topleft = pos)
class Ball(pygame.sprite.Sprite):
def __init__(self,groups,obstacles):
super().__init__(groups)
self.image = pygame.Surface((40,40))
self.image.fill('red')
self.rect = self.image.get_rect(center = (400,400))
# attributes for dt influenced movement
self.direction = pygame.math.Vector2((0.8,1))
self.speed = 6
self.obstacles = obstacles
def vertical_collision(self):
for sprite in self.obstacles.sprites():
if sprite.rect.colliderect(self.rect):
if self.direction.y < 0: # moving up
self.rect.top = sprite.rect.bottom
else: # moving down
self.rect.bottom = sprite.rect.top
self.direction.y *= -1
def horizontal_collision(self):
for sprite in self.obstacles.sprites():
if sprite.rect.colliderect(self.rect):
if self.direction.x < 0: # left
self.rect.left = sprite.rect.right
else: # right
self.rect.right = sprite.rect.left
self.direction.x *= -1
def wall_constraint(self):
if self.rect.right >= 800 or self.rect.left <= 0:
self.direction.x *= -1
if self.rect.bottom >= 800 or self.rect.top <= 0:
self.direction.y *= -1
def update(self):
self.wall_constraint()
self.rect.y += self.direction.y * self.speed
self.vertical_collision()
self.rect.x += self.direction.x * self.speed
self.horizontal_collision()
# setup
pygame.init()
screen = pygame.display.set_mode((800,800))
clock = pygame.time.Clock()
# sprite groups
all_sprites = pygame.sprite.Group()
collision_sprites = pygame.sprite.Group()
# sprite creation
Block((100,400),(60,200),[all_sprites,collision_sprites])
Block((700,600),(100,200),[all_sprites,collision_sprites])
Block((400,200),(200,100),[all_sprites,collision_sprites])
Block((600,300),(10,200),[all_sprites,collision_sprites])
Block((100,200),(100,100),[all_sprites,collision_sprites])
ball = Ball(all_sprites,collision_sprites)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
all_sprites.update()
all_sprites.draw(screen)
pygame.display.update()
clock.tick(60)
The problem I have is how to convert this to a format that works with deltatime, here is what I have so far:
import pygame,sys,time
class Block(pygame.sprite.Sprite):
def __init__(self,pos,size,groups):
super().__init__(groups)
self.image = pygame.Surface(size)
self.image.fill('yellow')
self.rect = self.image.get_rect(topleft = pos)
class Ball(pygame.sprite.Sprite):
def __init__(self,groups,obstacles):
super().__init__(groups)
self.image = pygame.Surface((40,40))
self.image.fill('red')
self.rect = self.image.get_rect(center = (400,400))
# attributes for dt influenced movement
self.pos = pygame.math.Vector2(self.rect.topleft)
self.direction = pygame.math.Vector2((0.8,1))
self.speed = 200
self.obstacles = obstacles
def vertical_collision(self):
for sprite in self.obstacles.sprites():
if sprite.rect.colliderect(self.rect):
if self.direction.y < 0: # moving up
self.pos.y = sprite.rect.bottom + 0.1
else: # moving down
self.pos.y = sprite.rect.top + self.rect.height - 0.1
self.rect.y = self.pos.y
self.direction.y *= -1
def horizontal_collision(self):
for sprite in self.obstacles.sprites():
if sprite.rect.colliderect(self.rect):
if self.direction.x < 0: # left
self.pos.x = sprite.rect.right + 0.1
else: # right
self.pos.x = sprite.rect.left - self.rect.width - 0.1
self.rect.x = self.pos.x
self.direction.x *= -1
def wall_constraint(self):
if self.rect.right >= 800 or self.rect.left <= 0:
self.direction.x *= -1
if self.rect.bottom >= 800 or self.rect.top <= 0:
self.direction.y *= -1
def update(self,dt):
self.wall_constraint()
self.pos.y += self.direction.y * self.speed * dt
self.vertical_collision()
self.pos.x += self.direction.x * self.speed * dt
self.horizontal_collision()
self.rect.x = round(self.pos.x)
self.rect.y = round(self.pos.y)
# setup
pygame.init()
screen = pygame.display.set_mode((800,800))
clock = pygame.time.Clock()
# sprite groups
all_sprites = pygame.sprite.Group()
collision_sprites = pygame.sprite.Group()
# sprite creation
Block((100,400),(60,200),[all_sprites,collision_sprites])
Block((700,600),(100,200),[all_sprites,collision_sprites])
Block((400,200),(200,100),[all_sprites,collision_sprites])
Block((600,300),(10,200),[all_sprites,collision_sprites])
Block((100,200),(100,100),[all_sprites,collision_sprites])
ball = Ball(all_sprites,collision_sprites)
last_time = time.time()
while True:
# delta time
dt = time.time() - last_time
last_time = time.time()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
all_sprites.update(dt)
all_sprites.draw(screen)
pygame.display.update()
clock.tick(120)
I am storing the position of the ball inside of a pos attribute (as a vector) and at the end of the update loop I am setting the rect position of the sprite to the x and y position of that pos vector. This is to account for pygame placing rects on integers.
The vertical collision seems to be working but the horizontal one really does not. I suspect it has to do with the position of the pos attribute and the resulting collision, I tried to give it an offset after it collided ( hence the + 0.1 or - 0.1) but that doesn't seem to make a difference.
In case someone is looking for the answer, my problem was the update method. It should have looked like this:
def update(self,dt):
self.wall_constraint()
# vertical movement + collision
self.pos.y += self.direction.y * self.speed * dt
self.rect.y = round(self.pos.y)
self.vertical_collision()
# horizontal movement + collision
self.pos.x += self.direction.x * self.speed * dt
self.rect.x = round(self.pos.x)
self.horizontal_collision()

How to shoot bullets from a character facing in direction of cursor in pygame?

In my game the problem is that bullets are coming only from one place i.e, from the center. As my player rotates in direction of cursor, I want the bullets to be shot from top of the player even if the player is rotated and travel in a straight line in the direction player is facing towards, As the player rotates in the direction of cursor.
As you can view here the the bullets are always in same direction and always come out of same place.
I tried to use getpos() method to get cursor position and tried to subtract from the player coordinates but failed to get the result.
I think the problem is within the def shoot(self) method of Rotator class, I need to get the coordinates spaceship's tip even when it is rotating all time.
import math
import random
import os
import pygame as pg
import sys
pg.init()
height=650
width=1200
os_x = 100
os_y = 45
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (os_x,os_y)
screen = pg.display.set_mode((width,height),pg.NOFRAME)
screen_rect = screen.get_rect()
background=pg.image.load('background.png').convert()
background = pg.transform.smoothscale(pg.image.load('background.png'), (width,height))
clock = pg.time.Clock()
running = True
class Mob(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('enemy.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('enemy.png'), (33,33))
self.rect = self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
self.speedx = random.randrange(-3, 3)
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 20:
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
class Rotator(pg.sprite.Sprite):
def __init__(self, screen_rect):
pg.sprite.Sprite.__init__(self)
self.screen_rect = screen_rect
self.master_image = pg.image.load('spaceship.png').convert_alpha()
self.master_image = pg.transform.smoothscale(pg.image.load('spaceship.png'), (33,33))
self.image = self.master_image.copy()
self.rect = self.image.get_rect(center=[width/2,height/2])
self.delay = 10
self.timer = 0.0
self.angle = 0
self.distance = 0
self.angle_offset = 0
def get_angle(self):
mouse = pg.mouse.get_pos()
offset = (self.rect.centerx - mouse[0], self.rect.centery - mouse[1])
self.angle = math.degrees(math.atan2(*offset)) - self.angle_offset
old_center = self.rect.center
self.image = pg.transform.rotozoom(self.master_image, self.angle,1)
self.rect = self.image.get_rect(center=old_center)
self.distance = math.sqrt((offset[0] * offset[0]) + (offset[1] * offset[1]))
def update(self):
self.get_angle()
self.display = 'angle:{:.2f} distance:{:.2f}'.format(self.angle, self.distance)
self.dx = 1
self.dy = 1
self.rect.clamp_ip(self.screen_rect)
def draw(self, surf):
surf.blit(self.image, self.rect)
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.centery)
all_sprites.add(bullet)
bullets.add(bullet)
class Bullet(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('bullet.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('bullet.png'), (10,10))
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.speedy = -8
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
all_sprites = pg.sprite.Group()
bullets = pg.sprite.Group()
mobs = pg.sprite.Group()
rotator = Rotator(screen_rect)
all_sprites.add(rotator)
for i in range(5):
m = Mob()
all_sprites.add(m)
mobs.add(m)
while running:
keys = pg.key.get_pressed()
for event in pg.event.get():
if event.type == pg.QUIT:
sys.exit()
pygame.quit()
if event.type == pg.MOUSEBUTTONDOWN:
rotator.shoot()
screen.blit(background, [0, 0])
all_sprites.update()
hits = pg.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
hits = pg.sprite.spritecollide(rotator, mobs, False)
if hits:
running = False
all_sprites.draw(screen)
clock.tick(60)
pg.display.update()
See Shooting a bullet in pygame in the direction of mouse and calculating direction of the player to shoot pygame.
Pass the mouse position to rotator.shoot(), when the mouse button is pressed:
if event.type == pg.MOUSEBUTTONDOWN:
rotator.shoot(event.pos)
Calculate the direction of from the rotator to the mouse position and pass it the constructor of the new bullet object:
def shoot(self, mousepos):
dx = mousepos[0] - self.rect.centerx
dy = mousepos[1] - self.rect.centery
if abs(dx) > 0 or abs(dy) > 0:
bullet = Bullet(self.rect.centerx, self.rect.centery, dx, dy)
all_sprites.add(bullet)
bullets.add(bullet)
Use pygame.math.Vector2 to store the current positon of the bullet and the normalized direction of the bullet (Unit vector):
class Bullet(pg.sprite.Sprite):
def __init__(self, x, y, dx, dy):
pg.sprite.Sprite.__init__(self)
self.image = pg.transform.smoothscale(pg.image.load('bullet.png').convert_alpha(), (10,10))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.speed = 8
self.pos = pg.math.Vector2(x, y)
self.dir = pg.math.Vector2(dx, dy).normalize()
Calcualate the new position of the bullet in update() (self.pos += self.dir * self.speed) and update the .rect attribute by the new position.
.kill() the bullet when it leaves the screen. This can be checked by self.rect.colliderect():
class Bullet(pg.sprite.Sprite):
# [...]
def update(self):
self.pos += self.dir * self.speed
self.rect.center = (round(self.pos.x), round(self.pos.y))
if not self.rect.colliderect(0, 0, width, height):
self.kill()

How would I make a group of sprites and randomly choose one?

this is my first post. I need to make a working game for my final project. Basically the idea is to have the character collide with an Item sprite and Have a random item display on the screen telling the player what the item is and where to take it.
#Initialize
import pygame
import random
pygame.init()
#Display
screen = pygame.display.set_mode((1180, 900))
class PaperBoy(pygame.sprite.Sprite):
def __init__(self,startY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("paperboy.gif")
self.rect = self.image.get_rect()
self.image = self.image.convert()
self.rect.centery = startY
self.dx= 300
self.dy= 300
def update(self):
#adjust x/y to dx/dy
self.rect.centerx = self.rect.centerx+self.dx
self.rect.centery = self.rect.centery+self.dy
#check Boundaries
#Check right
if self.rect.centerx >= 670:
self.rect.centerx =670
#Check left
elif self.rect.centerx <= 220:
self.rect.centerx = 220
#Check Bottom
if self.rect.centery >= 700:
self.rect.centery = 700
#Check Top
elif self.rect.centery <= 200:
self.rect.centery = 200
def moveUp(self):
self.dx=0
self.dy=-5
def moveDown(self):
self.dx =0
self.dy =5
def moveLeft(self):
self.dx =-5
self.dy = 0
def moveRight(self):
self.dx =5
self.dy =0
"""
mousex, mousey = pygame.mouse.get_pos()
self.rect.centery = mousey
#Check X boundary.
if mousex >= 670:
self.rect.right = 670
elif mousex <= 210:
self.rect.left = 210
else:
self.rect.centerx = mousex
#Check Y boundary.
if mousey >= 670:
self.rect.top = 670
elif mousey >= 250:
self.rect.top = 250
if mousex >= 250 and mousey >= 220:
self.rect.left = 250
self.rect.top = 670
else:
self.rect.centery = mousey
"""
class Parcel(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("parcel.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (200,600)
"""(random.randint(300,800)),(random.randint(300,800))"""
"""
================================HUD======================================
"""
#Green Y
class ItemHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("itemhud.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (950,200)
#Red A
class WhereHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("where.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (950, 350)
#Small Green
class TimeHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("timehud.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (915, 850)
#Yellow
class GoldHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("gold.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (915, 700)
"""
=================================HUD OBJECTS==============================
"""
"""
------------------------------------MAIN-----------------------------------
"""
def main():
pygame.display.set_caption("A Link to the Parcel")
background = pygame.image.load('village.png').convert()
allSprites=pygame.sprite.Group()
parcel = Parcel()
#Heads up Display
itemHud = ItemHUD()
timeHud = TimeHUD()
goldHud = GoldHUD()
whereHud = WhereHUD()
#Player
paperboy = PaperBoy(200)
#Sprites added to AllSprites Group
allSprites.add(paperboy)
allSprites.add(parcel)
allSprites.add(itemHud)
allSprites.add(timeHud)
allSprites.add(goldHud)
allSprites.add(whereHud)
font = pygame.font.Font(None, 25)
goldSack = 0
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.KEYUP:
if event.key==pygame.K_UP:
paperboy.moveUp()
elif event.key==pygame.K_DOWN:
paperboy.moveDown()
elif event.key==pygame.K_LEFT:
paperboy.moveLeft()
elif event.key==pygame.K_RIGHT:
paperboy.moveRight()
fontTitle = font.render("A Link to the Parcel", True, (255,255,255,))
screen.blit(background, (0, 0))
screen.blit(fontTitle, [925,100])
allSprites.clear(screen, background,)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
If I understand what you mean, you have a bunch of Sprite types, and you want to choose one at random. To do that, just put them all in a list, use random.choice to pick one, and then instantiate it. Like this:
class OneKindOfParcel(Parcel): # etc.
class AnotherKindOfParcel(Parcel): # etc.
class AThirdKindOfParcel(Parcel): # etc.
parcels = [OneKindOfParcel, AnotherKindOfParcel, AThirdKindOfParcel]
# … later …
parceltype = random.choice(parcels)
parcel = parceltype()
allSprites.add(parcel)
You probably want to give it a location, and display something about its name and location, and so on, but I think you know how to do all that.
Quick and dirty is a list of items and then use random.choice from the random module.

Categories