Why does rotated image move by itself? - python

I rotate an image and I adjust the rect to the rotated image. When I start the program, the image starts moving, without any order by the program. There are, for testing, some pictures needed. I hope, as more experienced users, can take some standards.
import pygame
import sys
from pygame.constants import KEYUP
from pygame.constants import KEYDOWN
import math
import random
pygame.init()
cell_size = 40
cell_number = 20
breite = int(cell_size * cell_number )
hoehe = int( cell_size * cell_number )
screen = pygame.display.set_mode((breite,hoehe))
clock = pygame.time.Clock()
anzahl_gegenstaende = 10 # gehört zu landschaft
class MeinAuto(pygame.sprite.Sprite):
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(auto_img,(30,66)).convert_alpha()
self.rect = self.image.get_rect(center=(x,y))
self.rect.x = x
self.rect.y = y
self.mask = pygame.mask.from_surface(self.image )
self.wagen_winkel = 0
self.speed = 5
self.links = False
self.rechts = False
self.vor = False
self.zurueck = False
self.lenk_winkel = 0
self.block_vor = False
self.block_zurueck = False
def update(self):
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if self.vor or self.zurueck:
self.links = True
if event.key == pygame.K_RIGHT:
if self.vor or self.zurueck:
self.rechts = True
if event.key == pygame.K_UP:
self.vor = True
if event.key == pygame.K_DOWN:
self.zurueck = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
self.links = False
if event.key == pygame.K_RIGHT:
self.rechts = False
if event.key == pygame.K_UP:
self.vor = False
if event.key == pygame.K_DOWN:
self.zurueck = False
self.winkel_berechnung(1)
dx = math.cos(math.radians(self.wagen_winkel))
dy = math.sin(math.radians(self.wagen_winkel))
if self.vor and self.block_vor == False:
self.rect.y -= int(self.speed * dx)
self.rect.x -= int(self.speed * dy)
self.block_zurueck = False
elif self.zurueck and self.block_zurueck == False:
self.rect.y += int(self.speed * dx)
self.rect.x += int(self.speed * dy)
self.block_vor = False
if self.links:
self.lenk_winkel +=5
self.lenk_winkel=min(self.lenk_winkel,120)
elif self.rechts:
self.lenk_winkel -=1
self.lenk_winkel=max(self.lenk_winkel,-120)
if not self.links and not self.rechts: self.lenk_winkel = 0
def winkel_berechnung(self,dt):
if self.rechts:
self.wagen_winkel += self.lenk_winkel
while self.wagen_winkel < 0:
self.wagen_winkel += 360
elif self.links:
self.wagen_winkel += self.lenk_winkel
while self.wagen_winkel > 359:
self.wagen_winkel -= 360
class Lenkung(pygame.sprite.Sprite):
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(lenkrad_img,(120,120)).convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.wagen_winkel = 0
class Landschaft(pygame.sprite.Sprite):
def __init__(self,image):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.mask = pygame.mask.from_surface(self.image )
x=random.randrange(60, breite -60)
y=random.randrange(200, hoehe - 200)
self.rect = self.image.get_rect(center =(x,y))
def zeichne_hintergrund():
background = pygame.image.load("Bilder/background_gelb.jpg")
screen.blit(background,(0,0))
def blitRotateCenter(image, left, top, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (left, top)).center)
screen.blit(rotated_image, new_rect)
return new_rect
########## Bilder laden
auto_img = pygame.image.load("Bilder/car.png")
lenkrad_img = pygame.image.load("bilder/lenkrad.png")
######### Gruppen bilden
auto = MeinAuto(breite/2,hoehe-100)
auto_sprite = pygame.sprite.Group()
auto_sprite.add(auto)
lenkung = Lenkung(breite/2,60)
lenkung_sprite = pygame.sprite.Group()
lenkung_sprite.add(lenkung)
land = pygame.sprite.Group()
while len(land) < anzahl_gegenstaende:
ii = len(land)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,100))
m = Landschaft(img)
if not pygame.sprite.spritecollide(m, land, False):
land.add(m)
while True:
clock.tick(6)
zeichne_hintergrund()
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
hits = pygame.sprite.spritecollide( auto, land, False, pygame.sprite.collide_mask)
for hit in hits:
pass
auto_sprite.update()
land.draw(screen)
new_auto_rect =blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
auto.rect=new_auto_rect
new_auto_rect = new_auto_rect.inflate(-80,-50)
blitRotateCenter(lenkung.image, lenkung.rect.x, lenkung.rect.y, auto.lenk_winkel)
pygame.draw.rect(screen,(0,250,0),auto.rect,4)
pygame.draw.rect(screen,(250,250,0),new_auto_rect,1)
pygame.display.flip()

Instead of the upper left corner of the image, you have to pass the center of the image to the blitRotateCenter function:
new_auto_rect = blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
new_auto_rect = blitRotateCenter(auto.image, auto.rect.centerx, auto.rect.centery, auto.wagen_winkel)
Simplify the code using the asterisk(*) operator:
new_auto_rect = blitRotateCenter(auto.image, *auto.rect.center, auto.wagen_winkel)

Related

How can I go back and forth between modules in pygame?

I'm trying to make a Home Screen for two games in pygame, I changed the first game's name to module1 in the code Im providing and the Home Screen to Mainscreen
module1 is already done and now I'm just trying to get the Main Menu script to import the maingame when I click on the play button that I already have on the screen display, then when I finish the game (health reaches 0) , if I want to go back to the Main Menu I should just click anywhere on the "game over" screen that should appear when health reaches 0 or press escape.
here is what I tried:
this is the whole game script, the game loop is at the end of the script and sorry for how messy it is:
# Pygame skeleton
import pygame
import math
import random
import sys
import os
pygame.init()
pygame.mixer.init()
vec = pygame.math.Vector2
# Constants
WIDTH = 1306
HEIGHT = 526
FPS_INGAME = 60
FPS_HOMESCREEN = 30
FULL_SCREEN = (0,0)
bullet_img = pygame.image.load('Bullet.png')
jump = False
bullet_timer = 0
score = 0
moving_left = False
moving_right = False
shoot = False
acceleration = 0.7
friction = -0.1
gravity = 9.8
platform_color = (150,59,230)
# Classes and Sprites
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.shoot_cooldown = 0
self.anime = []
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
self.action = 0
animation_types = ["Shoot","Go"]
for animation in animation_types:
temp_list = []
num_of_frames = len(os.listdir(f'MySprite/{animation}'))
for i in range(1,num_of_frames):
img = pygame.image.load(f'MySprite/{animation}/{i}.png')
temp_list.append(img)
self.anime.append(temp_list)
self.direction = 1
self.flip = False
self.image = self.anime[self.action][self.frame_index]
self.rect = self.image.get_rect()
# vectors and sprite constants
self.health = 100
self.pos = vec(WIDTH/2, HEIGHT/2)
self.vel_vec = vec(0,0)
self.acc_vec = vec(0,0)
def jump(self):
self.rect.y += 1
hits = pygame.sprite.spritecollide(player_group.sprite, platform_group,False)
self.rect.y -= -1
if hits:
self.vel_vec.y = -15
def screen_edge(self):
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
def get_damage(self,damage_mag):
self.health -= damage_mag
def update(self):
animation_cooldown = 75
self.image = self.anime[self.action][self.frame_index]
if pygame.time.get_ticks() - self.update_time >= animation_cooldown:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
if self.frame_index >= len(self.anime[self.action]):
self.frame_index = 0
if self.shoot_cooldown > 0:
self.shoot_cooldown -= 1
def update_action(self,new_action):
if new_action != self.action:
self.action = new_action
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def move(self,moving_left,moving_right):
self.acc_vec = vec(0,acceleration)
if moving_left:
self.acc_vec.x = -(acceleration)
self.direction = 1
self.flip = False
if moving_right:
self.acc_vec.x = acceleration
self.direction = -1
self.flip = True
self.acc_vec.x += self.vel_vec.x * friction
self.vel_vec += self.acc_vec
self.pos += self.vel_vec + 0.5 * self.acc_vec
self.rect.midbottom = self.pos
hits = pygame.sprite.spritecollide(player_group.sprite, platform_group,False)
if hits:
self.pos.y = hits[0].rect.top
self.vel_vec.y = 0
def draw(self):
screen.blit(pygame.transform.flip(self.image,self.flip,False),self.rect)
class Platform(pygame.sprite.Sprite):
def __init__(self,x,y,w,h):
super().__init__()
self.image = pygame.Surface((w,h))
self.image.fill(platform_color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Hostiles(pygame.sprite.Sprite):
def __init__(self,x_pos,y_pos,speed):
super(Hostiles,self).__init__()
self.images = []
self.images.append(pygame.image.load('MySprite/images/go_1.png'))
self.images.append(pygame.image.load('MySprite/images/go_2.png'))
self.images.append(pygame.image.load('MySprite/images/go_3.png'))
self.images.append(pygame.image.load('MySprite/images/go_4.png'))
self.images.append(pygame.image.load('MySprite/images/go_5.png'))
self.images.append(pygame.image.load('MySprite/images/go_6.png'))
self.images.append(pygame.image.load('MySprite/images/go_7.png'))
self.images.append(pygame.image.load('MySprite/images/go_8.png'))
self.images.append(pygame.image.load('MySprite/images/go_9.png'))
self.images.append(pygame.image.load('MySprite/images/go_10.png'))
self.index = 0
self.image = self.images[self.index]
self.rect = self.image.get_rect(center = (x_pos,y_pos))
self.speed = speed
def update(self):
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.image = self.images[self.index]
self.rect.centerx += self.speed
if self.rect.centerx >= 1350 or self.rect.centerx <= -50:
self.kill()
class Hostiles2(pygame.sprite.Sprite):
def __init__(self,x_pos,y_pos,speed):
super(Hostiles2,self).__init__()
self.images2 = []
self.images2.append(pygame.image.load('MySprite/images2/go_1.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_2.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_3.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_4.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_5.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_6.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_7.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_8.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_9.png'))
self.images2.append(pygame.image.load('MySprite/images2/go_10.png'))
self.index = 0
self.image = self.images2[self.index]
self.rect = self.image.get_rect(center = (x_pos,y_pos))
self.speed = speed
def update(self):
self.index += 1
if self.index >= len(self.images2):
self.index = 0
self.image = self.images2[self.index]
self.rect.centerx += self.speed
if self.rect.centerx >= 1350 or self.rect.centerx <= -50:
self.kill()
class Bullets(pygame.sprite.Sprite):
def __init__(self,x,y,direction):
super().__init__()
self.speed = 10
self.image = bullet_img
self.rect = self.image.get_rect(center = (x,y))
self.direction = direction
def update(self):
self.rect.x -= (self.direction * self.speed)
if self.rect.left >= WIDTH or self.rect.right <= 0:
self.kill()
screen.blit(self.image,self.rect)
# Functions
def make_text(font_type,font_size,text,color,position):
font = pygame.font.Font(font_type, font_size)
title = font.render(text,True,(color))
title_rect = title.get_rect(center = (position))
screen.blit(title,title_rect)
def main_game():
pygame.draw.rect(screen,(255,0,0),(22,20,200,10))
pygame.draw.rect(screen,platform_color,(22,20,2 * player_group.sprite.health,10))
screen.blit(heart,(0,2))
bullet_group.draw(screen)
player.draw()
hostiles_group.draw(screen)
platform_group.draw(screen)
bullet_group.update()
player_group.update()
hostiles_group.update()
player_group.update()
player.screen_edge()
if shoot:
if player.shoot_cooldown == 0:
bullet = Bullets(player.rect.centerx - (0.6 * player.direction * player.rect.size[0]),player.rect.centery,player.direction)
bullet_group.add(bullet)
player.shoot_cooldown = 40
if moving_left or moving_right:
player.update_action(1)
else:
player.update_action(0)
player.move(moving_left,moving_right)
if pygame.sprite.spritecollide(player_group.sprite,hostiles_group,True):
player_group.sprite.get_damage(10)
pygame.sprite.groupcollide(hostiles_group, bullet_group,True,True)
return 2
def game_over():
screen.fill((0,0,0))
text = gamefont.render("GAME OVER",True,(255,255,255))
text_rect = text.get_rect(center = (653,243))
screen.blit(text,text_rect)
scoresurface = gamefont.render(f"Score: {score}",True,(255,255,255))
score_rect = scoresurface.get_rect(center = (653,283))
screen.blit(scoresurface,score_rect)
# Creating window
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("game name")
clock = pygame.time.Clock()
# Groups and objects
gamefont = pygame.font.Font('Chernobyl.ttf',40)
player = Player()
platform = Platform(0,HEIGHT-96,WIDTH,100)
platform_group = pygame.sprite.GroupSingle()
platform_group.add(platform)
player_group = pygame.sprite.GroupSingle()
player_group.add(player)
hostiles_group = pygame.sprite.Group()
hostile_event = pygame.USEREVENT
pygame.time.set_timer(hostile_event,300)
bullet_group = pygame.sprite.Group()
pygame.mouse.set_visible(True)
sky = pygame.image.load('SkyNight.png')
wallpaper = pygame.image.load('GamePlatform.png')
heart = pygame.image.load('Heart.png')
# Game loop
running = True
while running:
# Events (Inputs)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == hostile_event:
random_xpos = [-40,-20]
random_xpos2 = [1340,1360]
random_hs = [-2,2]
hostile_left = Hostiles2(random.choice(random_xpos),400,random.choice(random_hs))
hostile_right = Hostiles(random.choice(random_xpos2),400,random.choice(random_hs))
hostiles_group.add(hostile_left,hostile_right)
if event.type == pygame.MOUSEBUTTONDOWN and player_group.sprite.health <= 0:
player_group.sprite.health = 100
hostiles_group.empty()
score = 0
import MainMenu
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
moving_left = True
if event.key == pygame.K_RIGHT:
moving_right = True
if event.key == pygame.K_q:
shoot = True
if event.key == pygame.K_ESCAPE:
running = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
moving_left = False
if event.key == pygame.K_RIGHT:
moving_right = False
if event.key == pygame.K_q:
shoot = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player_group.sprite.jump()
if player_group.sprite.health > 0:
score += main_game()
print (score)
else:
game_over()
pygame.display.update()
screen.blit(sky,FULL_SCREEN)
screen.blit(wallpaper,FULL_SCREEN)
programIcon = pygame.image.load('icon.png')
pygame.display.set_icon(programIcon)
clock.tick(FPS_INGAME)
and this is all the main screen / main menu .py file:
import pygame,sys
pygame.init()
screen = pygame.display.set_mode((1306,526))
clock = pygame.time.Clock()
pygame.mouse.set_visible(True)
screen_home = pygame.image.load('HomeScreen.png')
def make_text(font_type,font_size,text,color,position):
font = pygame.font.Font(font_type, font_size)
title = font.render(text,True,(color))
title_rect = title.get_rect(center = (position))
screen.blit(title,title_rect)
while True:
font1 = pygame.font.Font('Starjedi.ttf',60)
game1 = font1.render("Play",True,(255,255,255))
game1_rect = game1.get_rect(center = (640,300))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if game1_rect.collidepoint(event.pos):
import MainGame.py
screen.blit(screen_home,(0,0))
screen.blit(game1,game1_rect)
make_text('Chernobyl.ttf',70,"Title Here",(255,255,255),(315,80))
pygame.display.update()
clock.tick(45)
when I run the main menu I get the Home Screen I made, then I click on the on my display screen and it imports the game script and the game starts, when my health reaches 0 and I click on the screen to quit the game loop, it takes me back to the main menu which is exactly what I wanted,however, when I press play again to go back to the game it doesn't work it gives me an error:
ModuleNotFoundError: No module named 'MainGame.py'; 'MainGame' is not a package
I know the code is a mess I'm still new at this I'm sorry if my question wasn't clear enough, any help would be appreciated!
Ok, now that I see your code I can see you have a big problem using import:
You use it like this:
import MainGame.py
Assume the file you created is MainGame.py you supposed to import it this way:
import MainGame
Same goes to
import MainMenu.py
And that's might be the problem, though if you still have an issue, so instead of:
in the game, this is part of the main game loop:
Please copy all of the code, in both files, and write the name of each file next to the code.

Multiple sprite collision detections

Ok, so I have been trying to figure out how to set this up and can't get it working properly. I'm trying to get the functionality similar to the moving logs in the game Frogger if you are familiar with it. So far what I have works with the collision detection for only one pygame.sprite.Group()
import pygame
import os
import random
os.environ['SDL_VIDEO_CENTERED'] = '1'
WIDTH = 480
HEIGHT = 720
FPS = 60
# Moving Object spacing setup first row
INSTANCE_COUNT = 0
BOX_WIDTH = 30
SPACING = 120
# object speed
pos_objspeed = 1
neg_objspeed = -1
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 30))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 96
self.speedx = 0
self.speedy = 0
class MovingObj_Test(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((90, 30))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = (INSTANCE_COUNT * (SPACING+ BOX_WIDTH))
#self.rect.x = random.randrange(WIDTH)
self.rect.bottom = 384
self.speedx = pos_objspeed
def update(self):
self.rect.x += self.speedx
if self.rect.x >= 480:
self.rect.x = -30
self.rect.bottom = 384
self.speedx = pos_objspeed
class MovingObj_TestTwo(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((90, 30))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = (INSTANCE_COUNT * (SPACING+ BOX_WIDTH))
#self.rect.x = random.randrange(WIDTH)
self.rect.bottom = 336
self.speedx = neg_objspeed
def update(self):
self.rect.x += self.speedx
if self.rect.x >= 480:
self.rect.x = -30
self.rect.bottom = 336
self.speedx = neg_objspeed
all_sprites = pygame.sprite.Group()
movingobj_l = pygame.sprite.Group()
movingobj_r = pygame.sprite.Group()
for i in range (3):
INSTANCE_COUNT = i + 1
obj1 = MovingObj_Test()
all_sprites.add(obj1)
movingobj_l.add(obj1)
for i in range (3):
INSTANCE_COUNT = i + 1
obj2 = MovingObj_TestTwo()
all_sprites.add(obj2)
movingobj_r.add(obj2)
player = Player()
all_sprites.add(player)
running = True
onLogLeft = False
onLogRight = False
groundSpd = 48
while running:
clock.tick(FPS)
hitsleft = pygame.sprite.spritecollide(player, movingobj_l, False, pygame.sprite.collide_rect)
for hit in hitsleft:
player.speedx = pos_objspeed
player.speedy = 0
onLogLeft = True
if len(hitsleft) == 0:
onLogLeft = False
player.speedx = 0
player.speedy = 0
hitsright = pygame.sprite.spritecollide(player, movingobj_r, False, pygame.sprite.collide_rect)
for hit in hitsright:
player.speedx = neg_objspeed
player.speedy = 0
onLogRight = True
if len(hitsright) == 0:
onLogRight = False
player.speedx = 0
player.speedy = 0
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.speedx = -groundSpd
if event.key == pygame.K_RIGHT:
player.speedx = groundSpd
if event.key == pygame.K_UP:
player.speedy = -groundSpd
if event.key == pygame.K_DOWN:
player.speedy = groundSpd
if event.type == pygame.KEYUP and onLogLeft == False and onLogRight == False:
if event.key == pygame.K_LEFT:
player.speedx = 0
if event.key == pygame.K_RIGHT:
player.speedx = 0
if event.key == pygame.K_UP:
player.speedy = 0
if event.key == pygame.K_DOWN:
player.speedy = 0
if onLogLeft == False and onLogRight == False and player.rect.bottom <= 384:
running = False
player.rect.x += player.speedx
player.rect.y += player.speedy
if event.type == pygame.QUIT:
running = False
#player.rect.x += player.speedx
#player.rect.y += player.speedy
all_sprites.update()
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
quit()
Ok so in my code the
hitsright = pygame.sprite.spritecollide(player, movingobj_r, False, pygame.sprite.collide_rect)
checks the collision and updates the players speed how I'm expecting it to but, the
hitsleft = pygame.sprite.spritecollide(player, movingobj_l, False, pygame.sprite.collide_rect)
is not doing the same.
I'm confused as to why this is if anyone has any ideas. Also how could I fix it to work for both?
The code for left and right behaves different, because if a collision with hitsleft is detected, the player.speedx is set:
for hit in hitsleft:
player.speedx = pos_objspeed
But if there is a collision with hitsleft there it no collision with hitsright and player.speedx is reset immediately:
if len(hitsright) == 0:
onLogRight = False
player.speedx = 0
You have to set player.speedx dependent on both conditions (e.g. in a if - elif - else statement):
while running:
# [...]
hitsleft = pygame.sprite.spritecollide(player, movingobj_l, False, pygame.sprite.collide_rect)
onLogLeft = any(hitsleft)
hitsright = pygame.sprite.spritecollide(player, movingobj_r, False, pygame.sprite.collide_rect)
onLogRight = any(hitsright)
player.speedy = 0
if onLogLeft:
player.speedx = pos_objspeed
elif onLogRight:
player.speedx = neg_objspeed
else:
player.speedx = 0

How to get camera following a top down car in pygame

new to pygame and game programming in general, just wondered how I could get a camera to follow a car (nothing fancy) in a top down car game - think Micro Machines! I'm using Python 3.6, and have got a bike rotating, and moving around. I've kept the code here shorter but I do have a static image for reference if the camera worked!
Here's what I have:
import pygame, math, sys, random
from pygame.locals import *
display_width = 1280
display_height = 800
# Sets size of screen
screen = pygame.display.set_mode((display_width, display_height))
# Initialises clock
clock = pygame.time.Clock()
# Colours
white = (255,255,255)
black = (0,0,0)
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class VehicleSprite(Entity):
# Creates a vehicle class
MAX_FORWARD_SPEED = 10
MAX_REVERSE_SPEED = 2
ACCELERATION = 0.05
TURN_SPEED = 0.000000000001
def __init__(self, image, position):
Entity.__init__(self)
# Creates object instance off
pygame.sprite.Sprite.__init__(self)
self.src_image = pygame.image.load(image)
self.position = position
self.speed = self.direction = 0
self.k_left = self.k_right = self.k_down = self.k_up = 0
def update(self, time):
# SIMULATION
self.speed += (self.k_up +self.k_down)
if self.speed > self.MAX_FORWARD_SPEED:
self.speed = self.MAX_FORWARD_SPEED
if self.speed < -self.MAX_REVERSE_SPEED:
self.speed = -self.MAX_REVERSE_SPEED
# Degrees sprite is facing (direction)
self.direction += (self.k_right + self.k_left)
x, y = self.position
rad = self.direction * math.pi / 180
x += -self.speed*math.sin(rad)
y += -self.speed*math.cos(rad)
self.position = (x, y)
self.image = pygame.transform.rotate(self.src_image, self.direction)
self.rect = self.image.get_rect()
self.rect.center = self.position
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
rect = screen.get_rect()
# Background
BackGround = Background('/home/pi/gametuts/images/backgrounds/bkg_img.png', [0, 0])
# Bike image load
bike = VehicleSprite('/home/pi/gametuts/images/BikePixelBig.png', rect.center)
bike_group = pygame.sprite.RenderPlain(bike)
# Ball image load
ball = VehicleSprite('/home/pi/gametuts/images/ironball.png', rect.center)
ball_group = pygame.sprite.RenderPlain(ball)
# Main game loop
def game_loop():
while 1:
#USER INPUT
# Sets frame rate
time = clock.tick(60)
for event in pygame.event.get():
if not hasattr(event, 'key'): continue
down = event.type == KEYDOWN
# Bike Input (Player 1)
if event.key == K_d: bike.k_right = down * -5
elif event.key == K_a: bike.k_left = down * 5
elif event.key == K_w: bike.k_up = down * 2
elif event.key == K_s: bike.k_down = down * -2
# Quit
elif event.key == K_ESCAPE: sys.exit(0)
#RENDERING
# Game background
screen.fill(white)
screen.blit(BackGround.image, BackGround.rect)
# Bike render
bike_group.update(time)
bike_group.draw(screen)
ball_group.update(time)
ball_group.draw(screen)
pygame.display.flip()
game_loop()
pygame.quit()
quit()
Thanks in advance!
The simplest way to implement a camera is to use a pygame.math.Vector2 as the camera, subtract the player velocity from it each frame and add it to the position of all game elements during the blitting.
import pygame as pg
from pygame.math import Vector2
class Player(pg.sprite.Sprite):
def __init__(self, pos, walls, *groups):
super().__init__(*groups)
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('dodgerblue'))
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(0, 0)
self.pos = Vector2(pos)
self.walls = walls
self.camera = Vector2(0, 0)
def update(self):
self.camera -= self.vel # Change the camera pos if we're moving.
# Horizontal movement.
self.pos.x += self.vel.x
self.rect.centerx = self.pos.x
# Change the rect and self.pos coords if we touched a wall.
for wall in pg.sprite.spritecollide(self, self.walls, False):
if self.vel.x > 0:
self.rect.right = wall.rect.left
elif self.vel.x < 0:
self.rect.left = wall.rect.right
self.pos.x = self.rect.centerx
self.camera.x += self.vel.x # Also move the camera back.
# Vertical movement.
self.pos.y += self.vel.y
self.rect.centery = self.pos.y
for wall in pg.sprite.spritecollide(self, self.walls, False):
if self.vel.y > 0:
self.rect.bottom = wall.rect.top
elif self.vel.y < 0:
self.rect.top = wall.rect.bottom
self.pos.y = self.rect.centery
self.camera.y += self.vel.y
class Wall(pg.sprite.Sprite):
def __init__(self, x, y, w, h, *groups):
super().__init__(*groups)
self.image = pg.Surface((w, h))
self.image.fill(pg.Color('sienna2'))
self.rect = self.image.get_rect(topleft=(x, y))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
walls = pg.sprite.Group()
for rect in ((100, 170, 90, 20), (200, 100, 20, 140),
(400, 60, 150, 100), (300, 470, 150, 100)):
walls.add(Wall(*rect))
all_sprites.add(walls)
player = Player((320, 240), walls, all_sprites)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_d:
player.vel.x = 5
elif event.key == pg.K_a:
player.vel.x = -5
elif event.key == pg.K_w:
player.vel.y = -5
elif event.key == pg.K_s:
player.vel.y = 5
elif event.type == pg.KEYUP:
if event.key == pg.K_d and player.vel.x > 0:
player.vel.x = 0
elif event.key == pg.K_a and player.vel.x < 0:
player.vel.x = 0
elif event.key == pg.K_w and player.vel.y < 0:
player.vel.y = 0
elif event.key == pg.K_s and player.vel.y > 0:
player.vel.y = 0
all_sprites.update()
screen.fill((30, 30, 30))
for sprite in all_sprites:
# Add the player's camera offset to the coords of all sprites.
screen.blit(sprite.image, sprite.rect.topleft+player.camera)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Edit: Here's your code example with a camera. I've also tried to improve a few more things, for example the max(min(...)) trick to clamp the speed value. I'm not sure if the movement works as you want, but you can of course adjust it yourself. (I'd probably make even more modifications to the update method.)
import math
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 800))
rect = screen.get_rect()
clock = pygame.time.Clock()
WHITE = pygame.Color('white')
# Load images globally and reuse them in your program.
# Also use the `.convert()` or `.convert_alpha()` methods after
# loading the images to improve the performance.
VEHICLE1 = pygame.Surface((40, 70), pygame.SRCALPHA)
VEHICLE1.fill((130, 180, 20))
VEHICLE2 = pygame.Surface((40, 70), pygame.SRCALPHA)
VEHICLE2.fill((200, 120, 20))
BACKGROUND = pygame.Surface((1280, 800))
BACKGROUND.fill((30, 30, 30))
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class VehicleSprite(Entity):
MAX_FORWARD_SPEED = 10
MAX_REVERSE_SPEED = 2
ACCELERATION = 0.05
TURN_SPEED = 0.000000000001
def __init__(self, image, position):
Entity.__init__(self)
self.src_image = image
self.image = image
self.rect = self.image.get_rect(center=position)
self.position = pygame.math.Vector2(position)
self.velocity = pygame.math.Vector2(0, 0)
self.speed = self.direction = 0
self.k_left = self.k_right = self.k_down = self.k_up = 0
def update(self, time):
# SIMULATION
self.speed += self.k_up + self.k_down
# To clamp the speed.
self.speed = max(-self.MAX_REVERSE_SPEED,
min(self.speed, self.MAX_FORWARD_SPEED))
# Degrees sprite is facing (direction)
self.direction += (self.k_right + self.k_left)
rad = math.radians(self.direction)
self.velocity.x = -self.speed*math.sin(rad)
self.velocity.y = -self.speed*math.cos(rad)
self.position += self.velocity
self.image = pygame.transform.rotate(self.src_image, self.direction)
self.rect = self.image.get_rect(center=self.position)
class Background(pygame.sprite.Sprite):
def __init__(self, image, location):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.rect = self.image.get_rect(topleft=location)
def game_loop():
background = Background(BACKGROUND, [0, 0])
bike = VehicleSprite(VEHICLE1, rect.center)
ball = VehicleSprite(VEHICLE2, rect.center)
bike_group = pygame.sprite.Group(bike)
ball_group = pygame.sprite.Group(ball)
all_sprites = pygame.sprite.Group(bike_group, ball_group)
camera = pygame.math.Vector2(0, 0)
done = False
while not done:
time = clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
# Bike Input (Player 1)
if event.key == pygame.K_d:
bike.k_right = -5
elif event.key == pygame.K_a:
bike.k_left = 5
elif event.key == pygame.K_w:
bike.k_up = 2
elif event.key == pygame.K_s:
bike.k_down = -2
elif event.key == pygame.K_ESCAPE:
done = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_d:
bike.k_right = 0
elif event.key == pygame.K_a:
bike.k_left = 0
elif event.key == pygame.K_w:
bike.k_up = 0
elif event.key == pygame.K_s:
bike.k_down = 0
camera -= bike.velocity
all_sprites.update(time)
screen.fill(WHITE)
screen.blit(background.image, background.rect)
for sprite in all_sprites:
screen.blit(sprite.image, sprite.rect.topleft+camera)
pygame.display.flip()
game_loop()
pygame.quit()

Horizontal collisions when colliding with a block [duplicate]

This question already has answers here:
how to know pygame.Rect's side that collide to other Rect?
(1 answer)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
I successfully got my player sprite to detect where this is a block and stop falling. However, i cant seem to do the same for the its colliding side of with a block. If i do the same thing i did with vspeed and to it to hspeed it messes up the game and the characters zooms off the screen. I was trying to get my sprite to recognize the collision and then not go through the block. Any help would be appreciated. Thanks.
class Player(pygame.sprite.Sprite):
def __init__(self,x,y,width = 65, height = 35):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.hspeed,self.vspeed = 0,0
self.speed = 2
self.Jump = 10
self.images=[]
r0 = pygame.image.load("Images\Player\i1.png")
r1 = pygame.image.load("Images\Player\i2.png")
r2 = pygame.image.load("Images\Player\i3.png")
r3 = pygame.image.load("Images\Player\i4.png")
self.hurt = pygame.image.load("Images\Player\Hurt.png")
self.images.append(r0)
self.images.append(r1)
self.images.append(r2)
self.images.append(r3)
self.rotatedimages = []
rr0 = pygame.transform.flip(r0 ,True, False)
rr1 = pygame.transform.flip(r1 ,True, False)
rr2 = pygame.transform.flip(r2 ,True, False)
rr3 = pygame.transform.flip(r3 ,True, False)
self.rotatedimages.append(rr0)
self.rotatedimages.append(rr1)
self.rotatedimages.append(rr2)
self.rotatedimages.append(rr3)
self.deadimages = [self.hurt]
self.gravity = 0.35
self.index = 0
self.image = self.images[self.index]
self.rect = pygame.Rect(self.x,self.y,width,height)
self.TimeNum=0
self.TimeTarget=10
self.Timer = 0
self.power_up_timer = 0
self.running = False
def update(self):
self.calcgravity()
self.rect.x += self.hspeed
self.rect.y += self.vspeed
def level_3_collisions(self, BlockListDirt2):
PlatformCollision = pygame.sprite.spritecollide(self, BlockListDirt2, False )
for each_block in PlatformCollision:
if self.vspeed > 0:
self.rect.bottom = each_block.rect.top
self.vspeed = 0
if self.vspeed <0:
self.rect.top = each_block.rect.bottom
self.vspeed = 0
def move(self, hspeed, vspeed):
self.hspeed += hspeed
self.vspeed += vspeed
class Level3:
def __init__(self):
self.level3 = [
[1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1],
[1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1],
[1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1],
[1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1],
[1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1],
[1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]
for y in range(0,len(self.level3)):
for x in range(0,len(self.level3[y])):
if self.level3[y][x] == 1:
BlockListDirt2.add(Block(x*40,y*40))
def update(self):
for block in BlockListDirt2:
block.render2(screen)
def main3():
TrapList.empty()
TrapList.add(trap4)
player.hspeed = 0
player.rect.x,player.rect.y = 50,0
player.Timer = 0
bullet_list_v.empty()
bullet_list_h.empty()
FiringBullet = pygame.USEREVENT + 1
pygame.time.set_timer(FiringBullet, 1500)
GameExit = False
while GameExit==False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.move(-player.speed,0)
if event.key == pygame.K_RIGHT:
player.move(player.speed,0)
if event.key == pygame.K_UP:
player.move(0,-player.Jump)
JumpSound.play()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.move(player.speed,0)
if event.key == pygame.K_RIGHT:
player.move(-player.speed,0)
if event.key == pygame.K_UP:
player.move(0,0)
if event.type == FiringBullet:
bullet = VerticalBullets(400,200)
bullet2 = VerticalBullets(500,200)
bullet3 = VerticalBullets(625,200)
hbullet = HorizontalBullets(700,400)
bullet_list_v.add(bullet)
bullet_list_v.add(bullet2)
bullet_list_v.add(bullet3)
bullet_list_h.add(hbullet)
screen.fill(BLACK)
level3.update()
for eachbullet in bullet_list_v:
bullet_list_v.draw(screen)
bullet_list_v.update()
for eachbullet in bullet_list_h:
bullet_list_h.draw(screen)
bullet_list_h.update()
if player.rect.x > 600 and player.rect.y < 0:
NextLevel3()
TrapList.draw(screen)
trampoline.render()
playergroup.update()
playergroup.draw(screen)
player.level_3_collisions(BlockListDirt2)
pygame.display.update()
clock.tick(60)

Why can't I get my camera to work?

I tried to create a camera for my platformer using this tutorial and I can't get it work. It's probably (hopefully) something really easy to fix because I'm new to programming.
This is what it should do.
Instead it's just acting as if there is no camera implemented at all. The player sprite moves around the screen that's visible.
This is my code, if you want the entire thing so you can run it, I'd be happy to provide it:
class Camera(object):
def __init__(self, camera_func, width, height):
self.camera_func = camera_func
self.state = pygame.Rect(0, 0, width, height)
def apply(self, target):
return target.rect.move(self.state.topleft)
def update(self, target):
self.state = self.camera_func(self.state, target.rect)
def complex_camera(camera, target_rect):
l, t, _, _ = target_rect
_, _, w, h = camera
l, t, _, _ = -l+HALF_WIDTH, -t+HALF_HEIGHT, w, h
l = min(0, l) # stop scrolling at the left edge
l = max(-(camera.width-SCREEN_WIDTH), l) # stop scrolling at the right edge
t = max(-(camera.height-SCREEN_HEIGHT), t) # stop scrolling at the bottom
t = min(0, t) # stop scrolling at the top
return Rect(l, t, w, h)
return Rect(l, t, w, h)
class Sprite(pygame.sprite.Sprite):
def __init__(self,img):
pygame.sprite.Sprite.__init__(self)
class Player(Sprite):
def __init__(self,x,y,width,height):
self.dx = 0
self.dy = 0
self.onGround = False
self.image = pygame.image.load('robotStanding_sprite.png')
self.rect = pygame.Rect(x,y,width,height)
def update(self):
self.rect.x += self.dx
self.collide(self.dx,0,terrain_list)
self.rect.y += self.dy
self.collide(0,self.dy,terrain_list)
if moving_right: self.dx = SPEED
if moving_left: self.dx = -SPEED
if not(moving_left or moving_right): self.dx = 0
if moving_up:
self.dy = -SPEED
self.onGround = False
elif not moving_up: self.dy = SPEED
if self.rect.y >= LOWER_BOUNDARY-GROUND_LEVEL_HEIGHT-ROBOT_HEIGHT:
self.rect.y = LOWER_BOUNDARY-GROUND_LEVEL_HEIGHT-ROBOT_HEIGHT
self.onGround = True
def collide(self,xvel,yvel,blocks):
for block in blocks:
if pygame.sprite.collide_rect(player,block):
if xvel > 0: self.rect.right = block.rect.left
if xvel < 0: self.rect.left = block.rect.right
if yvel > 0:
self.rect.bottom = block.rect.top
self.onGround = True
class Terrain(Sprite):
def __init__(self,img,x,y,width,height):
self.image = pygame.image.load(img)
self.rect = pygame.Rect(x,y,width,height)
def addBlock(img,x,y,width,height):
block = Terrain(img,x,y,width,height)
terrain_list.append(block)
pygame.init()
size = [700,500]
screen = pygame.display.set_mode(size)
LEFT_BOUNDARY = 0
RIGHT_BOUNDARY = size[0]
UPPER_BOUNDARY = 0
LOWER_BOUNDARY = size[1]
HALF_WIDTH = size[0] / 2
HALF_HEIGHT = size[1] / 2
TILE_SIZE = 20
ROBOT_HEIGHT = 2*TILE_SIZE
ROBOT_WIDTH = 25
GROUND_HEIGHT = 100
GROUND_LEVEL_HEIGHT = 4*TILE_SIZE
GROUND_WIDTH = 3000
BLOCK_HEIGHT = 500
BLOCK_WIDTH = 160
SCREEN_WIDTH = 24*TILE_SIZE
SCREEN_HEIGHT = 35*TILE_SIZE
terrain_list = []
player = Player(17*TILE_SIZE,19*TILE_SIZE,ROBOT_WIDTH,ROBOT_HEIGHT)
Terrain.addBlock('ground.fw.png',
0*TILE_SIZE,21*TILE_SIZE,GROUND_WIDTH,GROUND_HEIGHT)
Terrain.addBlock('block1.fw.png',
-5*TILE_SIZE,0*TILE_SIZE,BLOCK_WIDTH,BLOCK_HEIGHT) # LEFT WALL
Terrain.addBlock('block1.fw.png',
24*TILE_SIZE,16*TILE_SIZE,BLOCK_WIDTH,BLOCK_HEIGHT)
Terrain.addBlock('block1.fw.png',
32*TILE_SIZE,20*TILE_SIZE,BLOCK_WIDTH,BLOCK_HEIGHT)
Terrain.addBlock('block1.fw.png',
40*TILE_SIZE,12*TILE_SIZE,BLOCK_WIDTH,BLOCK_HEIGHT)
# CREATE CAMERA -----------------------------------------------------------
camera = Camera(complex_camera, 24*TILE_SIZE, 3000)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d: moving_right = True
if event.key == pygame.K_a: moving_left = True
if event.key == pygame.K_w:
if player.onGround:
moving_up = True
if event.key == pygame.K_DOWN: moving_down = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_d: moving_right = False
if event.key == pygame.K_a: moving_left = False
if event.key == pygame.K_w: moving_up = False
screen.fill(SKY_BLUE)
camera.update(player)
player.update()
screen.blit(player.image,player.rect)
for block in terrain_list:
screen.blit(block.image,camera.apply(block))
# DRAW TILE LINES -----------------------------------------------------
if draw_tiles:
x = 0
y = 0
for i in range(RIGHT_BOUNDARY // 2):
pygame.draw.line(screen,BLACK,
[x,UPPER_BOUNDARY],[x,LOWER_BOUNDARY])
pygame.draw.line(screen,BLACK,
[LEFT_BOUNDARY,y],[RIGHT_BOUNDARY,y])
x += TILE_SIZE
y += TILE_SIZE
# tile test
pygame.draw.rect(screen,BLACK,
(34*TILE_SIZE,24*TILE_SIZE,TILE_SIZE,TILE_SIZE))
pygame.display.flip()
clock.tick(60)
pygame.quit()
Camera.apply(target) should probably be doing something that involves self.state instead of target.rect.move(1,1).

Categories