I dont know where this error is coming from - pygame

Im trying to play my code but i get this error 'video system not initialized' and i dont even know where that could be coming from. Here is my code so far:
#Imports
import sys as pygame
import pygame as pg
from random import randint, uniform
vec = pg.math.Vector2
#Player variables
WIDTH = 1200
HEIGHT = 660
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
#----------------------------------------------------------------------------
# Player properties
Player_SIZE = 32
MAX_SPEED = 5
MAX_FORCE = 0.5
APPROACH_RADIUS = 100
all_sprites = pg.sprite.Group()
class Player(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_mouse(self):
mpos = pg.mouse.get_pos()
self.acc = (mpos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (target - self.pos).normalize() * MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (target - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(pg.mouse.get_pos())
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED:
self.vel.scale_to_length(MAX_SPEED)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#Enemy properties
MAX_SPEED1 = 4.5
ppos = pg.mouse.get_pos()
class Enemy(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED1, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_player(self):
self.acc = (ppos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (ppos - self.pos).normalize() * MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (ppos() - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(Player)
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED1:
self.vel.scale_to_length(MAX_SPEED1)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#----------------------------------------------------------------------------
#Display
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()
pg.display.set_caption("Maze")
Icon = pg.image.load('Logo.png')
pg.display.set_icon(Icon)
#Main loop
all_sprites = pg.sprite.Group()
Player()
Enemy()
paused = False
running = True
while running:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
paused = not paused
if event.key == pg.K_m:
Player()
Enemy()
if not paused:
all_sprites.update()
screen.fill(DARKGRAY)
all_sprites.draw(screen)
pg.display.flip()
pg.quit()
I know it's long but Ireally dont know where the problem could be coming from.
Any help? (There is no problem with the indent im only having trouble pasting the code here)

You have to call pygame.init() before you can use pygame.mouse.get_pos(); or, in your case, pg.init(), since you imported pygame as pg.
I don't know why you import sys as pygame; that quite confusing.
Also, getting the mouse position once that the start of your game is not helpful. Everytime you want to know the mouse position, you should call .mouse.get_pos() again.

Related

Pygame joystick doesn't show multiple joysticks [duplicate]

import pygame
import os
import random
import time
import json
from pygame import joystick
pygame.font.init()
pygame.init()
WIDTH, HEIGHT = 1920, 1000
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("space invaders")
WHITE = (255, 255, 255)
#load images
RED_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_red_small.png")), (125, 100))
GREEN_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_green_small.png")), (125, 100))
BLUE_SPACE_SHIP = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "pixel_ship_blue_small.png")), (125, 100))
#player
YELLOW_SPACE_SHIP = pygame.transform.rotate(pygame.transform.scale(pygame.image.load(os.path.join("Assets", "spaceship_yellow.png")), (154, 121)), 180)
#lasers
RED_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_red.png"))
BLUE_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_blue.png"))
GREEN_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_green.png"))
#player laser
YELLOW_LASER = pygame.image.load(os.path.join("Assets", "pixel_laser_yellow.png"))
#background
BG = pygame.transform.scale(pygame.image.load(
os.path.join('Assets', 'space.png')), (WIDTH, HEIGHT))
class Laser():
def __init__(self, x, y, img):
self.x = x
self.y = y
self.img = img
self.mask = pygame.mask.from_surface(self.img)
def draw(self, window):
window.blit(self.img, (self.x, self.y))
def move(self, vel):
self.y += vel
def off_screen(self, height):
return not(self.y <= height and self.y >= 0)
def collision(self, obj):
return collide(self, obj)
class Ship:
COOLDOWN = 30
def __init__(self, x, y, health = 100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
window.blit(self.ship_img, (self.x, self.y))
for laser in self.lasers:
laser.draw(window)
def move_lasers(self, vel, obj):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
elif laser.collision(obj):
obj.health -= 10
self.lasers.remove(laser)
def cooldown(self):
if self.cool_down_counter >= self.COOLDOWN:
self.cool_down_counter = 0
elif self.cool_down_counter > 0:
self.cool_down_counter += 1
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
class Player(Ship):
def __init__(self, x, y, health = 100):
super().__init__(x, y, health)
self.ship_img = YELLOW_SPACE_SHIP
self.laser_img = YELLOW_LASER
self.mask = pygame.mask.from_surface(self.ship_img)
self.max_health = health
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x + 26, self.y - 57, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def move_lasers(self, vel, objs):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
else:
for obj in objs:
if laser.collision(obj):
objs.remove(obj)
if laser in self.lasers:
self.lasers.remove(laser)
def draw(self, window):
super().draw(window)
self.healthbar(window)
def healthbar(self, window):
pygame.draw.rect(window, (255, 0, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width(), 10))
pygame.draw.rect(window, (0, 255, 0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width()* (self.health/self.max_health),10))
class Enemy(Ship):
COLOR_MAP = {
"red": (RED_SPACE_SHIP, RED_LASER),
"green": (GREEN_SPACE_SHIP, GREEN_LASER),
"blue": (BLUE_SPACE_SHIP, BLUE_LASER)
}
def __init__(self, x, y, color, health = 100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x - 3, self.y + 20, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
def main():
run = True
FPS = 60
level = 0
lives = 5
lost = False
lost_count = 0
main_font = pygame.font.SysFont("comicsans", 75)
lost_font = pygame.font.SysFont("comicsans", 500)
enemies = []
wave_length = 5
player_vel = 7
laser_vel = 7
enemy_vel = 1
player = Player(WIDTH // 2, 650)
clock = pygame.time.Clock()
def redraw_window():
WIN.blit(BG, (0,0))
#draw text
level_label = main_font.render(f"Level: {level}", 1, WHITE)
lives_label = main_font.render(f"Lives: {lives}", 1, WHITE)
WIN.blit(lives_label, (10, 10))
WIN.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))
player.draw(WIN)
for enemy in enemies:
enemy.draw(WIN)
if lost:
lost_label = lost_font.render("You Lost!", 1, (WHITE))
WIN.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, HEIGHT / 2 - lost_label.get_height() / 2))
pygame.display.update()
joysticks = []
for i in range(pygame.joystick.get_count()):
joysticks.append(pygame.joystick.Joystick(i))
for joystick in joysticks:
pygame.joystick.init()
print(pygame.joystick.get_init())
with open(os.path.join("ps4_keys.json"), 'r+') as file:
button_keys = json.load(file)
# 0: Left analog horizonal, 1: left analog verticle, 2: right analog horizonal
# 3: right analog verticle, 4: left Triger, 5: Right Trigger
analog_keys = {0:0, 1:0, 2:0, 3:0, 4:-1, 5:-1}
while run:
clock.tick(FPS)
redraw_window()
if lives <= 0 or player.health <= 0:
lost = True
lost_count += 1
if lost:
if lost_count > FPS * 3:
run = False
else:
continue
if len(enemies) == 0:
level += 1
wave_length += 5
for i in range(wave_length):
enemy = Enemy(random.randrange(100, WIDTH-100), random.randrange(-1500, -100), random.choice(["red", "blue", "green"]))
enemies.append(enemy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.JOYAXISMOTION:
analog_keys[event.axis] = event.value
print(analog_keys)
if abs(analog_keys[0]) > .4:
if analog_keys[0] < -.7:
player.x -= 7
else:
continue
if analog_keys[0] < .7:
player.x += 7
for enemy in enemies[:]:
enemy.move(enemy_vel)
enemy.move_lasers(laser_vel, player)
if random.randrange(0, 120) == 1:
enemy.shoot()
if collide(enemy, player):
player.health -= 10
enemies.remove(enemy)
elif enemy.y + enemy.get_height() > HEIGHT:
lives -= 1
enemies.remove(enemy)
player.move_lasers(-laser_vel, enemies)
def main_menu():
title_font = pygame.font.SysFont("comicsans", 150)
run = True
while run:
WIN.blit(BG, (0,0))
title_label = title_font.render("Click the mouse to begin...", 1, WHITE)
WIN.blit(title_label, (WIDTH / 2 - title_label.get_width() / 2, HEIGHT / 2 - title_label.get_height() / 2))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
main_menu()
^^^
so this is my code (here is the JSON file as well):
{
"x": 0,
"circle": 1,
"square": 2,
"triangle": 3,
"share": 4,
"PS": 5,
"options": 6,
"left_stick_click": 7,
"right_stick_click": 8,
"L1": 9,
"R1": 10,
"up_arrow": 11,
"down_arrow": 12,
"left_arrow": 13,
"right_arrow": 14,
"touchpad": 15
}
I'm trying to make the player be controlled by the controller left joystick and it returns no errors but my player does not move and it is printing true from the print(pygame.joystick.get_init()) and printing the joystick amounts from: print(analog_keys) but the player does not move. Any idea why?
Do not use the JOYAXISMOTION event. The event does not occur continuously, it only occurs once when the axis changes.
Use pygame.joystick.Joystick.get_axis to get the current position of the axis and move the player depending on the axis:
def main():
# [...]
while run:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if joysticks:
joystick = joysticks[0]
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
player.x += round(7 * axis_x)
if abs(axis_y) > 0.1:
player.y += round(7 * axis_y)
# [...]
Minimal example:
import pygame
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
x, y = window.get_rect().center
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
x = (x + round(7 * axis_x)) % window.get_width()
if abs(axis_y) > 0.1:
y = (y + round(7 * axis_y)) % window.get_height()
window.fill(0)
pygame.draw.circle(window, (255, 0, 0), (x, y), 10)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

Puzzled by my sprite's unequal +/ - velocity

I have two sprites in my game. The zombie sprite works perfectly, moving in all directions at a velocity of 1.0. My player sprite however, despite moves more slowly in the positive x/y direction, despite all values in all directions being 2.25.
For the life of me I can't seem to see what is wrong here.
Full working code:
import pygame
import random
import sys
import itertools
import math
import time
from datetime import datetime
from librarymodified import *
from pygame.locals import *
# prints text using the supplied font
def print_text(font, x, y, text, color=(255,255,255)):
imgText = font.render(text, True, color)
DISPLAYSURF.blit(imgText, (x,y))
# MySprite class extends pygame.sprite.Sprite
class MySprite(pygame.sprite.Sprite):
def __init__(self, target):
pygame.sprite.Sprite.__init__(self) #extend the base Sprite class
self.master_image = None
self.frame = 0
self.old_frame = -1
self.frame_width = 1
self.frame_height = 1
self.first_frame = 0
self.last_frame = 0
self.columns = 1
self.last_time = 0
self.direction = 0
self.times_hit = 0
self.direction = 0
self.velocity = Point(0.0,0.0)
# times_hit property
def _get_times_hit(self): return self.times_hit
def _set_times_hit(self, hits): self.times_hit += hits
number_hits_taken = property(_get_times_hit, _set_times_hit)
#X property
def _getx(self): return self.rect.x
def _setx(self,value): self.rect.x = value
X = property(_getx,_setx)
#Y property
def _gety(self): return self.rect.y
def _sety(self,value): self.rect.y = value
Y = property(_gety,_sety)
#position property
def _getpos(self): return self.rect.topleft
def _setpos(self,pos): self.rect.topleft = pos
position = property(_getpos,_setpos)
def load(self, filename, width, height, columns):
self.master_image = pygame.image.load(filename).convert_alpha()
self.frame_width = width
self.frame_height = height
self.rect = Rect(0,0,width,height)
self.columns = columns
#try to auto-calculate total frames
rect = self.master_image.get_rect()
self.last_frame = (rect.width // width) * (rect.height // height) - 1
def update(self, current_time, rate=30):
#update animation frame number
if current_time > self.last_time + rate:
self.frame += 1
if self.frame > self.last_frame:
self.frame = self.first_frame
self.last_time = current_time
#build current frame only if it changed
if self.frame != self.old_frame:
frame_x = (self.frame % self.columns) * self.frame_width
frame_y = (self.frame // self.columns) * self.frame_height
rect = Rect(frame_x, frame_y, self.frame_width, self.frame_height)
self.image = self.master_image.subsurface(rect)
self.old_frame = self.frame
def __str__(self):
return str(self.frame) + "," + str(self.first_frame) + \
"," + str(self.last_frame) + "," + str(self.frame_width) + \
"," + str(self.frame_height) + "," + str(self.columns) + \
"," + str(self.rect)
#Point class
class Point(object):
def __init__(self, x, y):
self.__x = x
self.__y = y
#X property
def getx(self): return self.__x
def setx(self, x): self.__x = x
x = property(getx, setx)
#Y property
def gety(self): return self.__y
def sety(self, y): self.__y = y
y = property(gety, sety)
def __str__(self):
return "{X:" + "{:.0f}".format(self.__x) + \
",Y:" + "{:.0f}".format(self.__y) + "}"
def calc_velocity(direction, vel = 1.0):
velocity = Point(0, 0)
if direction == 0: # North
velocity.y = -vel
elif direction == 2: # East
velocity.x = vel
elif direction == 4: # south
velocity.y = vel
elif direction == 6: # west
velocity.x = -vel
return velocity
def reverse_direction(sprite):
if sprite.direction == 0:
sprite.direction = 4
elif sprite.direction == 2:
sprite.direction = 6
elif sprite.direction == 4:
sprite.direction = 0
elif sprite.direction == 6:
sprite.direction = 2
# main
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800,600))
pygame.display.set_caption("Collision Detection")
font = pygame.font.SysFont(None, 36)
fpsclock = pygame.time.Clock()
fps = 30
# create sprite groups
zombie_group = pygame.sprite.Group()
player_group = pygame.sprite.Group()
health_group = pygame.sprite.Group()
# create player sprite
player = MySprite(DISPLAYSURF)
player.load("farmer walk.png", 96, 96, 8)
player.position = (80,80)
player.direction = 4
player_group.add(player)
# create zombie sprite
zombie_image = pygame.image.load("zombie walk.png").convert_alpha()
for i in range(1):
zombie = MySprite(DISPLAYSURF)
zombie.load("zombie walk.png", 96, 96, 8)
zombie.position = (random.randint(0, 700), random.randint(0, 500))
zombie.direction = random.randint(0,3) * 2
zombie_group.add(zombie)
# create health sprite
health = MySprite(DISPLAYSURF)
health.load("health.png", 32, 32, 1)
health.position = (400, 300)
health_group.add(health)
game_over = False
player_moving = False
player_health = 100
# colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 0)
YELLOW = (255, 255, 0)
##DISPLAYSURF.fill(BLACK)
##pygame.mouse.set_visible(True)
# event loop
while True:
ticks = pygame.time.get_ticks() # ms since pygame.init() called
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEMOTION:
mousex, mousey = event.pos
# keyboard polling
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
pygame.quit()
sys.exit()
elif keys[K_UP] or keys[K_w]:
player.direction = 0
player_moving = True
elif keys[K_RIGHT] or keys[K_d]:
player.direction = 2
player_moving = True
elif keys[K_LEFT] or keys[K_a]:
player.direction = 6
player_moving = True
elif keys[K_DOWN] or keys[K_s]:
player.direction = 4
player_moving = True
else:
player_moving = False
# these things should not happen if game is over
if not game_over:
# update player sprite
player_group.update(ticks, 50)
# use player direction to calculate frame range
player.first_frame = player.direction * player.columns
player.last_frame = player.first_frame + player.columns-1
if player.frame < player.first_frame:
player.frame = player.first_frame
if not player_moving:
# stop animating when player is not moving
player.frame = player.first_frame = player.last_frame
else:
# move player in that direction
player.velocity = calc_velocity(player.direction, 1.5)
player.velocity.x *= 1.5
player.velocity.y *= 1.5
# manually move player
if player_moving:
player.X += player.velocity.x
player.Y += player.velocity.y
if player.X <0: player.X = 0
elif player.X > 700: player.X = 700
if player.Y <0: player.Y = 0
elif player.Y > 500: player.Y = 500
# update zombie sprites
zombie_group.update(ticks, 50)
# manually update zombies
for z in zombie_group:
# set zombie animation range
z.first_frame = z.direction * z.columns
z.last_frame = z.first_frame + z.columns-1
if z.frame < z.first_frame:
z.frame = z.first_frame
z.velocity = calc_velocity(z.direction)
# keep zombie on screen
z.X += z.velocity.x
z.Y += z.velocity.y
if z.X < 0 or z.X > 700 or z.Y < 0 or z.Y > 500:
reverse_direction(z)
# check for sprite collision
attacker = 0
attacker = pygame.sprite.spritecollideany(player, zombie_group)
if attacker != None:
# more precise check
if pygame.sprite.collide_rect_ratio(0.5)(player, attacker):
player_health -= 10
if attacker.X < player.X: attacker.X -= 10
elif attacker.X > player.X: attacker.X += 10
else:
attacker = None
# update health drop
health_group.update(ticks, 50)
# check for collision with health
if pygame.sprite.collide_rect_ratio(0.5)(player, health):
player_health += 30
if player_health >100: player_health = 100
health.X = random.randint(0, 700)
health.Y = random.randint(0, 500)
# is player dead?
if player_health <= 0:
game_over = True
# clear screen
DISPLAYSURF.fill((50,50,100))
# draw sprites
player_group.draw(DISPLAYSURF)
zombie_group.draw(DISPLAYSURF)
health_group.draw(DISPLAYSURF)
# draw energy bar
pygame.draw.rect(DISPLAYSURF, WHITE, (299, 555, 203, 31), 2)
pygame.draw.rect(DISPLAYSURF, GREEN, (301, 557, player_health * 2, 28))
# print zombie and player velocities for purpose of testing
print_text(font, 350, 460, "Zombie X vel: " +\
str(zombie.velocity.x) + "\nY vel: " +\
str(zombie.velocity.y))
print_text(font, 350, 500, "Player X vel: " +\
str(player.velocity.x) + "\nY vel: " +\
str(player.velocity.y))
if game_over:
print_text(font, 300, 200, "G A M E O V E R")
pygame.display.update()
fpsclock.tick(fps)
Problem
The sprite group is drawing your sprite using the sprite's rect attribute. A pygame Rect object can only hold integers, so it'll truncate all floating point numbers.
Let's say you have a x = 5.
If you add 1.1: x += 1.1 <=> x = x + 1.1 <=> x = 5 + 1.1 <=> x = 6.1 which will be truncated to x = 6. It have increased by 1.
If you subtract 1.1: x -= 1.1 <=> x = x - 1.1 <=> x = 5 - 1.1 <=> x = 3.9 which will be truncated to x = 3. It have decreased by 2.
In other words: You'll move faster in the left direction than the right (the same principle applies to negative numbers). Here's an example demonstrating it:
import pygame
pygame.init()
class Player(pygame.sprite.Sprite):
def __init__(self, group):
super(Player, self).__init__(group)
self.image = pygame.Surface((32, 32))
self.rect = self.image.get_rect()
screen = pygame.display.set_mode((100, 100))
group = pygame.sprite.Group()
player = Player(group)
clock = pygame.time.Clock()
while True:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
x = player.rect.x + 1.1
print("Actual x:", x)
player.rect.x = player.rect.x + 1.1
print("Truncated x:", player.rect.x)
elif event.key == pygame.K_LEFT:
x = player.rect.x - 1.1
print("Actual x:", x)
player.rect.x = player.rect.x - 1.1
print("Truncated x:", player.rect.x)
screen.fill((255, 255, 255))
group.draw(screen)
pygame.display.update()
Solution
Using floating point numbers for position is great; it makes it possible to move a sprite less than a pixel every frame (if your game updates 120 times per second and you want your sprite to move only 30 pixels per second).
However, you have to compensate for the fact that the rect objects cannot hold them. The most straightforward solution is to have an attribute position which keep track of the position of the sprite in floating point precision. Then at every update change the rect to the position of the attribute. Like this:
import pygame
pygame.init()
class Player(pygame.sprite.Sprite):
def __init__(self, group):
super(Player, self).__init__(group)
self.image = pygame.Surface((32, 32))
self.rect = self.image.get_rect()
self.position = self.rect.x # Or whatever point of the rect you want the position to be.
screen = pygame.display.set_mode((100, 100))
group = pygame.sprite.Group()
player = Player(group)
clock = pygame.time.Clock()
while True:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.position += 1.1
player.rect.x = player.position
elif event.key == pygame.K_LEFT:
player.position -= 1.1
player.rect.x = player.position
screen.fill((255, 255, 255))
group.draw(screen)
pygame.display.update()
I've only showed how this movement works in the x-axis, but it's exactly the same on the y-axis.
Ok. I think the problem is that the number of pixels that the sprite is moved each update is rounded down, so that 2.25 becomes 2 pixels and -2.25 becomes -3 pixels. Moving by a fractional number of pixels doesn't make sense I think.
If you change lines 229 - 233 to
else:
# move player in that direction
player.velocity = calc_velocity(player.direction, 2.0)
player.velocity.x *= 2.0
player.velocity.y *= 2.0
The velocity is now an integer and there would be no rounding problems. It is faster though. Is there some reason why you don't just have the velocity as an integer instead of a float squared?

pygame collissions between two groups

I still have a problem with my software to check collisions between spaceship and asteroids. I have got no idea why I get a collision only in the top left corner of the screen.
any ideas ? any help please ?
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.x = posX
self.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.y > SCREENH:
self.y = (0 - self.rect[3])
if self.y < (0 - self.rect[3]):
self.y = SCREENH
if self.x > SCREENW:
self.x = (0 - self.rect[2])
if self.x < (0 - self.rect[2]):
self.x = SCREENW
self.x += self.dx
self.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].x,ASTEROIDS[i].y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()
The pygame.sprite.groupcollide() function finds collisions between all sprites of the two passed sprite-groups and returns a dictionary containing the collision information, as the documentation states.
Because the collision is determined by comparing the sprite.rect attribute of each sprite, every time you call an update() method of an sprite instance you need to update the position of self.rect object, instead of changing its self.x and self.y attributes.
Your Ship class is ok, because you change the self.rect object and its own .x or .y attributes. (e.g. self.rect[1] = (0 - self.rect[3])).But in your AsteroidSprite class you create a rect object in the __init__() method and only change the self.x and self.y attributes of an instance when you call the .update() method.
What you need to change:
The .update() method of the AsteroidSprite class, because you need to change the self.rect objects position, which is used for collision detection.
The if statement where you check for a collision, because pygame.sprite.groupcollide() returns a dict object, not a Boolean value.
I hope this helps you a little bit :)
Many thanks. Your answer helped me to solve the problem.
updated code:
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.rect.x = posX
self.rect.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.rect.y > SCREENH:
self.rect.y = (0 - self.rect[3])
if self.rect.y < (0 - self.rect[3]):
self.rect.y = SCREENH
if self.rect.x > SCREENW:
self.rect.x = (0 - self.rect[2])
if self.rect.x < (0 - self.rect[2]):
self.rect.x = SCREENW
self.rect.x += self.dx
self.rect.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].rect.x,ASTEROIDS[i].rect.y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()

AtrributeError with basic sprite collision

Just started with sprite collision in pygame. When this code is run, an AttributeError pops up that says ''Group' object has no attribute 'rect''. I can't figure out why this error occurs. Suggestions?
from random import randint
import pygame
pygame.init()
white = [255,255,255]
blue = [0,0,255]
red = [255,0,0]
size = (400,400)
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Simple character')
clock = pygame.time.Clock()
class Ball(pygame.sprite.Sprite):
def __init__(self, xDelta, yDelta, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(white)
self.image.set_colorkey(white)
self.xDelta = xDelta
self.yDelta = yDelta
self.color = color
self.rect = self.image.get_rect()
pygame.draw.circle(self.image, self.color,
[(self.rect.x + 10), (self.rect.y + 10)], 10)
def update(self):
self.rect.x += self.xDelta
self.rect.y += self.yDelta
if self.rect.y <= 0 or self.rect.y >= 380:
self.yDelta *= -1
if self.rect.x >= 380 or self.rect.x <= 0:
self.xDelta *= -1
allSprites = pygame.sprite.Group()
blues = pygame.sprite.Group()
reds = pygame.sprite.Group()
for i in range(5):
circle = Ball(randint(1, 10), randint(1, 10), blue)
circle.rect.x = randint(0,380)
circle.rect.y = randint(0,380)
blues.add(circle)
allSprites.add(circle)
circle2 = Ball(randint(1, 10), randint(1, 10), red)
circle2.rect.x = randint(0,380)
circle2.rect.y = randint(0,380)
reds.add(circle2)
allSprites.add(circle2)
play = True
while play:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
allSprites.update()
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0
screen.fill(white)
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
You are calling spritecollide on two objects of class Group. The function takes a Sprite and a Group.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
What you could do is loop through all the blues sprites, and call spritecollide with one blue ball.
for blueball in blues.sprites:
collision = pygame.sprite.spritecollide(blues, reds, True)
for circle in collision:
circle.xDelta = circle.yDelta = 0

Trying to rotate a guided missile in pygame

Trig is not my strong suit which is why I am having probs trying to get my guided missile to rotate properly. Looks like I have the direction fine although Im still not 100% happy with the movement, so Im hoping by fixing the rotation it will fix that also. Problem is my missiles dont point towards the target (player), and they also flip at a certain point past the target. Heres is the code...the rotations just dont work correctly.
def update(self):
self.calcVect()
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
self.normX = self.vectX/self.length
self.normY = self.vectY/self.length
self.dx += self.normX*self.power
self.dy += self.normY*self.power
def calcPos(self):
self.x += self.dx*0.2
self.y += self.dy*0.2
def rotate(self):
radians = math.atan2(-self.vectY,self.vectX)
radians %= 2*math.pi
self.dir = math.degrees(radians)
print self.dir
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMissile, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
Working Code
Here is the new working code for the entire class. The expression at the end of this line enabled correct rotations "radians = math.atan2(self.vectX, self.vectY)- math.pi/2" even though the image was horizontal to start with.
class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source):
pygame.sprite.Sprite.__init__(self)
self.screen = source.screen
self.imageMissile = pygame.image.load("Images/missile.tga").convert()
self.imageMissile.set_colorkey((0,0,255))
self.image = self.imageMissile
self.rect = self.image.get_rect()
self.rect.center = source.rect.midbottom
self.rect.inflate_ip(-15, -5)
self.x, self.y = self.rect.center
self.X, self.Y = self.rect.center
self.dx = 0
self.dy = 0
self.power = 3
self.dir = 0
def update(self):
self.player = goodSpritesOneGRP.sprite
self.targetX, self.targetY = self.player.rect.center
NX, NY = self.calcVect()
self.calcVel(NX, NY)
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
if self.length == 0:
self.normX = 0
self.normY = 1
else:
self.normX = self.vectX/self.length
self.normY = self.vectY/self.length
return (self.normX, self.normY)
def calcVel(self,NX,NY ):
self.dx += NX*self.power
self.dy += NY*self.power
def calcPos(self):
self.x += self.dx*0.05
self.y += self.dy*0.05
def rotate(self):
radians = math.atan2(self.vectX, self.vectY)- math.pi/2
radians %= 2*math.pi
self.dir = math.degrees(radians)
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMissile, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def checkBounds(self):
global guidedMissile
screen = self.screen
if self.x > screen.get_width():
self.kill()
guidedMissile -= 1
if self.x < -100:
self.kill()
guidedMissile -= 1
if self.y > screen.get_height() + 100:
self.kill()
guidedMissile -= 1
if self.y < -100:
self.kill()
guidedMissile -= 1
When you call calcPos() you have to recalculate self.vectX and self.vectY, otherwise the missile will not point to the target. And don't forget to check if self.length == 0
You can fix splitting calcVect():
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
if self.length != 0:
normX = vectX/self.length
normY = vectY/self.length
# Update velocity
def calcVel(self):
self.dx += self.normX*self.power
self.dy += self.normY*self.power
Then
def update(self):
self.calcVect()
self.calcVel()
self.calcPos()
self.calcVect()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
This solution works, but I suggest you to write a generic method calcVectTo(self, target) that returns the unitary vector pointing to (target.x, target.y):
def calcVect(self, target):
vectX = target.x - self.x
vectY = target.y - self.y
length = math.sqrt((vectX*vectX)+(vectY*vectY))
if length == 0:
normX = 0
normY = 1
else
normX = vectX/length
normY = vectY/length
return (normX, normY)
Edit
I did not understand that the target is fixed, now it is much easier: you only need to calculate vect at the construction of the class, moving calcVect from update to __construct (ignore my second implementation of calcVect):
class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source, target):
...
self.calcVect()
def update(self):
self.calcVel()
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)