Related
I have a problem I'm stuck now for last two days. In file game_function in nested loop I'm creating aliens, when I added randint to create random numbers of aliens in a row I run in to problem. Not always (just re-run the game) but sometimes when I detect sprite edge as a method of Alien class Aliens/sprites won't change the direction, follow x axis to the right and constantly dropping down Aliens each pass of check edge. I don't know what the heck is that. Before when I haven't been using randit to generate random numbers of aliens in a row, everything was just fine.
settings.py
class Settings():
"""A class to store all settings for Alien Invasion"""
def __init__(self):
"""Initialize the game settings"""
#Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230,230,230)
#ship settings
self.ship_speed_factor = 1.5
#Bullet settings
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
# Alien settings
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
ship.py
import pygame
class Ship():
def __init__(self,ai_settings, screen):
"""Initialize the ship and sets the starting position."""
self.screen = screen
self.ai_settings = ai_settings
#load the ship image and get its rect
self.image = pygame.image.load('images/ship.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new ship at the bottom center of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
#Movement Flag
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on the movement Flag."""
#Update the ship's center value, not the rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
#update rect object from self.center
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location"""
self.screen.blit(self.image, self.rect)
In a function create_fleet(ai_settings, screen, ship, aliens) is nested loop, when instead of generating random number between 3-9 and then placing new instance in row I've used constant calculation which always gives 9 aliens in row everything runned just fine. Aliens changed everytime directions and just one time dropped down until next check_edge event passed the condition. So from one wall to another. Now when the randint is in place, NOT ALWAYS, when check_edge method confirms true, then call to function change_fleet_direction() is made and there I see problem, it just sometimes doesn't change the direction. where for direction is used just simple +1 or -1 and in calling for update in Alien class it should either decrease x axis or increase until edge event.
game_functions.py
import sys
import pygame
from bullet import Bullet
from alien import Alien
from random import randint
def create_fleet(ai_settings, screen, ship, aliens):
"""Create a full fleet of aliens."""
#Create an Alien and find the number of aliens in a row
name= 'First unused'
alien = Alien(ai_settings, screen,name)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)
# Create the fleet of aliens.
for row_number in range(number_rows):
random_num = randint(3, number_aliens_x)
for alien_number in range(0, random_num):
create_alien(ai_settings, screen, aliens, alien_number,
row_number)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""Determine the number of rows of aliens that fit on the screen."""
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""Create alien and place it in the row"""
name = "Alien number " + str(alien_number) + " in row " + str(row_number)
alien = Alien(ai_settings, screen, name)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to key presses"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event,ship):
"""Respond to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""Respond to keypress and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(bullets):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def check_fleet_edges(ai_settings, aliens):
"""Respond appropriately if any aliens have reached an edge."""
for alien in aliens.sprites():
if alien.check_edges():
print(alien.name)
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
print("old direction " + str(ai_settings.fleet_direction))
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
if ai_settings.fleet_direction == 1:
ai_settings.fleet_direction = -1
else:
ai_settings.fleet_direction = 1
print("new direction" + str(ai_settings.fleet_direction))
def update_aliens(ai_settings, aliens):
"""
Check if the fleet is at an edge,
and then update the positions of all aliens in the fleet.
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
Main file alien_invasion.py
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# Initialize game and create a screen object.
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(ai_settings,screen)
#Make a group to store bullets in
bullets = Group()
aliens = Group()
#Create the fleet of aliens
gf.create_fleet(ai_settings, screen, ship, aliens)
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_bullets(bullets)
gf.update_aliens(ai_settings, aliens)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship"""
def __init__(self, ai_settings, screen, ship):
"""create a bullet object at the ship's current position"""
super().__init__()
self.screen = screen
#Create a bullet rect at (0, 0) and then set correct position
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
#Store the bullet position as a decimal value.
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""Move bullet up the scereen"""
#Update the decimal position of the bullet
self.y -= self.speed_factor
#Update the rect position
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen"""
pygame.draw.rect(self.screen, self.color, self.rect)
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A class to represent a single alien in the fleet"""
def __init__(self, ai_settings, screen, name):
"""Initialize the alien and set its starting position"""
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
#Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
#Start each alien near the top left of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#Store the alien's exact position
self.x = float(self.rect.x)
self.name = name
# def blitme(self):
# """Draw the alien at its current location."""
# self.screen.blit(self.image, self.rect)
#
def check_edges(self):
"""Return True if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
print("Right >= right screen" +' direction is' + str(self.ai_settings.fleet_direction))
return True
elif self.rect.left <= 0:
print("Left <= left")
return True
def update(self):
"""Move the alien right or left."""
self.x += (self.ai_settings.alien_speed_factor *
self.ai_settings.fleet_direction)
self.rect.x = self.x
I'm new to pygame and sprites, I don't understand it why randit has such impact. I've placed in the code when crucial changes are about to be made print statements to debug it from console (but I dunno if it is logic problem). Anyone who can shed a light to it I would appreciate so much. Lost 2 days of learning and programming already. Running python 3.4 from Eclipse Neon on win 10 Thanks very much.
Found the mistake, indentation in func change_fleet_direction(), if block: cannot be in for loop.
Modifying the Squirrel Eat Squirrel Pygame for a class project. Trying to add in a menu, with basic Start, Quit and Settings buttons. I have the buttons there, and they will light up as the mouse scrolls over them. However, I can't figure out how to call their respective functions when I click on them. I am trying to call them by this:
def runMenu(self):
mainloop = True
while mainloop:
self.__clock.tick(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
mainloop == False
if event.type == MOUSEBUTTONDOWN:
for item in self.__items:
#print(item)
if item.isMouseSelecting(pygame.mouse.get_pos()):
print(self.__functions)
self.__functions[item]() #HERE <----
I believe it is because I am using a dictionary in my if name == "main" (See below) therefore it's not actually the dictionary, but its referencing its location? If that makes sense. (i'm not very good at explaining it, so sorry)
if __name__ == "__main__":
screen = pygame.display.set_mode((640, 480), 0 , 32)
menuItems = ('Start', 'Quit', 'Settings')
functions = {'Start': main, 'Quit': terminate, 'Settings': None}
pygame.display.set_caption('Main Menu')
game = MainMenu(screen, functions, menuItems)
game.runMenu()
The error it gives me in shell:
>>> ================================ RESTART ================================
>>>
({'Start': <function main at 0x2969ab0>, 'Settings': None, 'Quit': <function terminate at 0x2969cf0>}, '#')
{'Start': <function main at 0x2969ab0>, 'Settings': None, 'Quit': <function terminate at 0x2969cf0>}
Traceback (most recent call last):
File "/Users/tjleggz/Documents/CS 110/squirrel/squirrel.py", line 501, in <module>
game.runMenu()
File "/Users/tjleggz/Documents/CS 110/squirrel/squirrel.py", line 138, in runMenu
self.__functions[item]() #cant call bc its not same object, just a ref to object or something???#
KeyError: <__main__.MenuItem object at 0x29758c8>
>>>
THANKS!
EDIT:
class MenuItem(pygame.font.Font):
def __init__(self, text, font=None, fontSize=30,
fontColor=(255, 255, 255), (posX, posY)=(0, 0)):
pygame.font.Font.__init__(self, font, fontSize) #initializes font module
self.__text = text
self.__fontSize = fontSize
self.__fontColor = fontColor
self.label = self.render(self.__text, 1, self.__fontColor) #not private??
self.width = self.label.get_rect().width
self.height = self.label.get_rect().height
self.__posX = posX
self.__posY = posY
self.position = posX, posY
def set_position(self, x, y):
self.position = (x, y)
self.__posX = x
self.__posY = y
def isMouseSelecting(self, (posX, posY)): #change to conditional?!
if (posX >= self.__posX and posX <= self.__posX + self.width) and \
(posY >= self.__posY and posY <= self.__posY + self.height):
return True
else:
return False
def setFontColor(self, rgbTuple):
self.fontColor = rgbTuple
self.label = self.render(self.__text, 1, self.fontColor)
Your self.items contains MenuItem objects, not strings which are the keys to your dictionary. You can work around this in a couple ways, for example you could use the __text attribute of MenuItem which is I think what you are looking for.
A better way to do this (instead of using a dictionary of functions like you are now) might be to pass an on_click function to each MenuItem instance you make, and then you just have to write
if item.isMouseSelecting(pygame.mouse.get_pos()):
item.on_click()
however you seem to be relatively new to programming, so you may just want to use the first suggestion.
Ok so for our couse,we have pretty much been thrown alot of code and told to make a project of it...been fixing bugs all week.Now i finally got a scrolling background working,but when I add it.The shoot functionality stops working,as well as the controls...I need help as to why this is happening? The project is due in a week and I'm running out of options..
""" Import al the necessary classes/sprites and pygame libs """
import pygame
from sys import exit
from LaserSprite import Laser
from PlayerShipSprite import PlayerShip
from EnenySprite import Asteroid
from Vector_Calc import Vector
from CursorSprite import Cursor
from ScoreBoardSprite import ScoreBoard
import math
""" Set up the screen size and sound mixer """
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
""" This function updates the score board with
information retrieved from the player class """
def updateScoreBoard(RefreshScore):
(ship, scoreBoard) = RefreshScore
scoreBoard.text = ("Score: %d Fired: %d Shield: %d" %(ship.Get_Score(),ship.Get_Ammo(),ship.Get_Shield()))
""" This function updates the position of the ship on the
screen based on what keys are pressed """
def checkKeys(myData):
(event, ship) = myData
if event.key == pygame.K_LEFT:
print 'LEFT'
ship.MoveLeft()
if event.key == pygame.K_RIGHT:
print 'RIGHT'
ship.MoveRight()
if event.key == pygame.K_UP:
ship.MoveUp()
print 'UP'
if event.key == pygame.K_DOWN:
print 'DOWN'
ship.MoveDown()
""" This is the main game loop, when the game is over the player will
be returned to the introduction screen """
def game():
""" Set up the title of the game, the background size, color and then
blit to the screen """
pygame.display.set_caption("Asteroids Version 2.0")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
""" Set the mouse cursor to be hidden """
pygame.mouse.set_visible(False)
""" Create a new instance of a player and cursor, here
I have set up the cursor as a class the will be updated during the game """
ship = PlayerShip(screen)
cursor = Cursor()
""" Create a new instance of the ScoreBoard class, this is then
given a text value and a position on the screen. Pay note
as to how the score, ammo and shield are retrieved from the PlayerShip class"""
scoreBoard = ScoreBoard()
scoreBoard.text = ("Score: %d Fired: %d Shield: %d" %(ship.Get_Score(),ship.Get_Ammo(),ship.Get_Shield()))
scoreBoard.center = (320,470)
""" Create empty sprite groups as shown below """
all_sprites_list = pygame.sprite.Group()
laser_list = pygame.sprite.Group()
asteroid_list = pygame.sprite.Group()
""" Create 20 asteroids and add them to the asteroid list group """
for i in range(10):
asteroid = Asteroid()
asteroid_list.add(asteroid)
""" Add the ship, cursor, asteroid list group and the scoreBoard to the
all sprites list. In doing this we can have the transparent effect some
were looking for """
all_sprites_list.add(ship)
all_sprites_list.add(cursor)
all_sprites_list.add(asteroid_list)
all_sprites_list.add(scoreBoard)
""" Set up the refresh rate and key repeat for the game"""
clock = pygame.time.Clock()
pygame.key.set_repeat(10,10)
keepGoing = True
while keepGoing:
b1 = "ocean1.gif"
back = pygame.image.load(b1)
back2 = pygame.image.load(b1)
h = 0
screenWidth = 600
clock.tick(30)
""" Check for any events - keyboard, mouse etc. """
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
screen.blit(back, (h,0))
screen.blit(back2, (h-screenWidth,0))
h = h + 0.5
if h == screenWidth:
h = 0
if event.type == pygame.KEYDOWN:
""" If a key is pressed, bundle the event and the ship
and pass it to the function that handles events """
myData = (event, ship)
checkKeys(myData)
if event.type == pygame.MOUSEBUTTONDOWN:
""" If a mouse event occurs check to see if it is the left click """
if event.button==1:
""" reduce the ammo of the ship
get the center of the ship sprite
get where the mouse was clicked on the screen
bundle the (x,y) position for the above
pass them to PlotVector function and have the
angle and vector returned
create a new instance a laser
pass to it the start position, angle and vector it must travel
add the laser to the laser_list group and the all sprites group """
ship.Set_Ammo();
shipCenter = ship.returnPosition()
mousePos = pygame.mouse.get_pos()
print "Ship: %s, Mouse: %s " %(shipCenter, mousePos)
data= (shipCenter, mousePos)
angle, vect = PlotVector(data)
laser = Laser()
laser.fire.play()
laser.AnglePoints(shipCenter, angle, vect)
laser_list.add(laser)
all_sprites_list.add(laser)
""" update all sprites """
all_sprites_list.update()
""" For every laser that was fired we are going to do the following: """
for laser in laser_list:
""" Create a list of asteroids that were hit by the laser """
asteroid_hit_list = pygame.sprite.spritecollide(laser, asteroid_list, False)
""" For each asteroid hit,
Update the ship score
play a bang sound
reset the asteroid to the top of the screen again
remove the laser from the laser list and all sprites """
for asteroid in asteroid_hit_list:
ship.Set_Score()
asteroid.bang.play()
asteroid.reset()
laser_list.remove(laser)
all_sprites_list.remove(laser)
""" Remove the laser if it flies up off the screen """
if laser.rect.y < -10 or laser.rect.y > screen.get_height():
laser_list.remove(laser)
all_sprites_list.remove(laser)
if laser.rect.x < -10 or laser.rect.x > screen.get_width():
laser_list.remove(laser)
all_sprites_list.remove(laser)
""" Now we are going to create a list of asteroids that hit the ship """
asteroid_hit_Ship = pygame.sprite.spritecollide(ship, asteroid_list, False)
""" For each asteroid that hits the ship,
Update the ship shields
play a bang sound
reset the asteroid to the top of the screen again """
for asteroid in asteroid_hit_Ship:
asteroid.bang.play()
ship.Set_Shield()
asteroid.reset()
""" if the ship's shields are less than 0 then the game will finish """
if ship.Get_Shield()<0:
keepGoung=False
break
""" Update the score and refresh the scoreboard using the updateScoreBoard function """
RefreshScore = (ship, scoreBoard)
updateScoreBoard(RefreshScore)
""" The last part refreshes the sprites """
asteroid_list.clear(screen, background)
asteroid_list.update()
asteroid_list.draw(screen)
all_sprites_list.clear(screen, background)
all_sprites_list.update()
all_sprites_list.draw(screen)
pygame.display.flip()
return ship.Get_Score()
def PlotVector(PlotVect):
"""
This PlotVector function handles the calculation of the new
vector and also the calculation of the angle the arrow/bullet/projectile
will be transformed and travel to.
REFER TO YOUR NOTES FROM LECTURE 5
"""
""" Un-bundle the data that is passed into this function """
(start, dest) = PlotVect
""" Create a new Vector object and call it vect """
vect = Vector()
""" Pass the start and dest coordinates and get back a new vector which the arrow must travel """
vect = Vector.from_points(start, dest)
""" Calculate the magnitude (Distance) between the two points """
mag = vect.get_magnitude()
""" Get the values for the vector, i.e. the change in x and change in y """
x = vect.x
y = vect.y
""" This variable will be used to calculate and store the angle between points """
angDEG = (math.atan2(y, x)*(180/math.pi))*-1
""" Print the coordinates and angle to the screen for testing """
print "Start: %s \nEnd: %s\nVector: %s\nMagnitude: %d" %(start, dest, vect, mag)
print "Angle : %d" %(angDEG)
""" Bundle and return the angle and vector which will be used in the TheArrow Sprite """
return (angDEG, vect)
"""
The next if statement runs the main function as it is in the primary program scope.
This was covered in lecture 5 available on Moodle
"""
def instructions(score):
shipIntro = PlayerShip(screen)
allSprites = pygame.sprite.Group(shipIntro)
insFont = pygame.font.SysFont(None, 30)
instructions = (
"Asteroids Version 2.0" ,
"Previous Score: %d" %score,
"Instructions: You must clear the asteroid",
"field so that you can reach your home-world",
"",
"Fly around the field and fire to hit the ",
"asteroids but be careful not to fly too close",
"to any asteroids. Your ship has a limited amount of ",
"off shielding and if your shielding drops below ",
"0 your done for",
"good luck!",
"",
"click to start, escape to quit..."
)
insLabels = []
for line in instructions:
tempLabel = insFont.render(line, 1, (255, 255, 0))
insLabels.append(tempLabel)
keepGoing = True
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
while keepGoing:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
donePlaying = True
if event.type == pygame.MOUSEBUTTONDOWN:
keepGoing = False
donePlaying = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
keepGoing = False
donePlaying = True
allSprites.update()
allSprites.draw(screen)
for i in range(len(insLabels)):
screen.blit(insLabels[i], (50, 30*i))
pygame.display.flip()
pygame.mouse.set_visible(True)
return donePlaying
def main():
donePlaying = False
score = 0
while not donePlaying:
donePlaying = instructions(score)
if not donePlaying:
score = game()
if __name__ == "__main__":
main()
Check indentions - you have if event.type == pygame.KEYDOWN: and if event.type == pygame.MOUSEBUTTONDOWN: outside of for event in pygame.event.get():. You have some screen.blit() betwin for and if with smaller indentions.
Maybe you put this in wrong place:
screen.blit(back, (h,0))
screen.blit(back2, (h-screenWidth,0))
h = h + 0.5
if h == screenWidth:
h = 0
EDIT:
Probably your code should look like this:
while keepGoing:
b1 = "ocean1.gif"
back = pygame.image.load(b1)
back2 = pygame.image.load(b1)
h = 0
screenWidth = 600
clock.tick(30)
""" Check for any events - keyboard, mouse etc. """
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.KEYDOWN:
""" If a key is pressed, bundle the event and the ship
and pass it to the function that handles events """
myData = (event, ship)
checkKeys(myData)
elif event.type == pygame.MOUSEBUTTONDOWN:
""" If a mouse event occurs check to see if it is the left click """
if event.button == 1:
""" reduce the ammo of the ship
get the center of the ship sprite
get where the mouse was clicked on the screen
bundle the (x,y) position for the above
pass them to PlotVector function and have the
angle and vector returned
create a new instance a laser
pass to it the start position, angle and vector it must travel
add the laser to the laser_list group and the all sprites group """
ship.Set_Ammo();
shipCenter = ship.returnPosition()
mousePos = pygame.mouse.get_pos()
print "Ship: %s, Mouse: %s " %(shipCenter, mousePos)
data= (shipCenter, mousePos)
angle, vect = PlotVector(data)
laser = Laser()
laser.fire.play()
laser.AnglePoints(shipCenter, angle, vect)
laser_list.add(laser)
all_sprites_list.add(laser)
screen.blit(back, (h,0))
screen.blit(back2, (h-screenWidth,0))
h = h + 0.5
if h == screenWidth:
h = 0
# rest of main loop
btw: I use elif because event.type can't be pygame.QUIT, pygame.KEYDOWN and pygame.MOUSEBUTTONDOWN in one event.
I have cairo+rsvg rendering a .svg file in pygame. But the color channels are wrong.
testing with lion.svg
But image is:
I believe I have my RGBA channel order swapped, (He's pink, not yellow). but am not clear on how it works. Here's my code, (which otherwise is rendering right.)
Maybe pygame.display.set_mode(...) or pygame.image.frombuffer(...) is the relevant problem?
import pygame
from pygame.locals import *
import os
import cairo
import rsvg
import array
WIDTH, HEIGHT = 60,60
class Lion(object):
"""load+draw lion.svg"""
def __init__(self, file=None):
"""create surface"""
# Sprite.__init__(self)
self.screen = pygame.display.get_surface()
self.image = None
self.filename = 'lion.svg'
self.width, self.height = WIDTH, HEIGHT
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
svg.render_cairo(ctx)
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")
def draw(self):
"""draw to screen"""
if self.image is None: self.draw_svg()
self.screen.blit(self.image, Rect(200,200,0,0))
class GameMain(object):
"""game Main entry point. handles intialization of game and graphics, as well as game loop"""
done = False
color_bg = Color('black') # or also: Color(50,50,50) , or: Color('#fefefe')
def __init__(self, width=800, height=600):
pygame.init()
self.width, self.height = width, height
self.screen = pygame.display.set_mode(( self.width, self.height ))
# self.screen = pygame.display.set_mode(( self.width, self.height ),0,32) # 32bpp for format 0x00rrggbb
# fps clock, limits max fps
self.clock = pygame.time.Clock()
self.limit_fps = True
self.fps_max = 40
self.lion = Lion()
def main_loop(self):
while not self.done:
# get input
self.handle_events()
self.draw()
# cap FPS if: limit_fps == True
if self.limit_fps: self.clock.tick( self.fps_max )
else: self.clock.tick()
def draw(self):
"""draw screen"""
self.screen.fill( self.color_bg )
self.lion.draw()
pygame.display.flip()
def handle_events(self):
"""handle events: keyboard, mouse, etc."""
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT: self.done = True
# event: keydown
elif event.type == KEYDOWN:
if event.key == K_ESCAPE: self.done = True
if __name__ == "__main__":
print """Keys:
ESC = quit
"""
game = GameMain()
game.main_loop()
Indeed - the byte order for each channel is different from cairo to pygame.
You can either juggle with the array before converting it to a string, to post the data in the correct order for pygame (you'd have to swap the green and blue data for each pixel) - or use
an aditional dependency: Python's PIL, to properly handle the different data at native speeds.
There is an snippet for that in pygame's site:
def bgra_surf_to_rgba_string(cairo_surface):
# We use PIL to do this
img = Image.frombuffer(
'RGBA', (cairo_surface.get_width(),
cairo_surface.get_height()),
cairo_surface.get_data(), 'raw', 'BGRA', 0, 1)
return img.tostring('raw', 'RGBA', 0, 1)
...
# On little-endian machines (and perhaps big-endian, who knows?),
# Cairo's ARGB format becomes a BGRA format. PyGame does not accept
# BGRA, but it does accept RGBA, which is why we have to convert the
# surface data. You can check what endian-type you have by printing
# out sys.byteorder
data_string = bgra_surf_to_rgba_string(cairo_surface)
# Create PyGame surface
pygame_surface = pygame.image.frombuffer(
data_string, (width, height), 'RGBA')
Check the whole recipe at: http://www.pygame.org/wiki/CairoPygame
If you don't want to add an aditional dependence (PIL in this case), Python's native arrays allows slice assignment - with those it is possible to swap the data of your blue and green channels at native speeds - try this swapping (may need some tunning :-) this is what worked for me after loading your image above as a png file, not from a cairo context )
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
blue = data[1::4]
green = data[3::4]
data[1::4] = green
data[3::4] = blue
svg.render_cairo(ctx)
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")
If the array slicing is not working, may I offer the PIL approach?
It was inspired by the same Pygame recipe jsbueno mentioned, but I cleaned it up and organized to a self-contained function. It takes a filename as argument, and return a pygame.Surface just like pygame.image.load() would.
import pygame # python-pygame
import rsvg # python-rsvg
import cairo # python-cairo
import PIL.Image # python-imaging
def load_svg(filename):
''' Load an SVG file and return a pygame.Surface '''
def bgra_rgba(surface):
''' Convert a Cairo surface in BGRA format to a RBGA string '''
img = PIL.Image.frombuffer(
'RGBA', (surface.get_width(), surface.get_height()),
surface.get_data(), 'raw', 'BGRA', 0, 1)
return img.tostring('raw', 'RGBA', 0, 1)
svg = rsvg.Handle(filename)
width, height = svg.props.width, svg.props.height
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
svg.render_cairo(cairo.Context(surface))
return pygame.image.frombuffer(bgra_rgba(surface), (width,height), "RGBA")
If you do not want to use PIL, the correct array slicing is:
def draw_svg(self):
"""draw .svg to pygame Surface"""
svg = rsvg.Handle(file= os.path.join('data', self.filename))
dim = svg.get_dimension_data()
self.width , self.height = dim[0], dim[1]
data = array.array('c', chr(0) * self.width * self.height * 4 )
cairo_surf= cairo.ImageSurface.create_for_data( data,
cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4 )
ctx = cairo.Context(cairo_surf)
svg.render_cairo(ctx)
blue = data[0::4]
red = data[2::4]
data[0::4] = red
data[2::4] = blue
self.image = pygame.image.frombuffer(data.tostring(), (self.width,self.height), "ARGB")
I'm trying to write this tutorial in Pygame(Python) and having problems about holding points between planes.
My code is: fiz2.py
Vector class: vector.py
If you move mouse on the Pygame screen, the planes will rotate. And when the planes are rotating, points are passing through planes and going outside.
I tried to fix points' positions on every iteration but they still passed the planes. I have no idea about where should I fix their positions.
NOTE: I know my code is a little bit messy, this is my first 2d program and I had really hard times getting used to Pygame's coordinate plane and vectors. I will re-write when I solve this.
NOTE2: Yes, I wrote the comment about how to hold points between planes on the tutorial, I understand the way he fixes positions but have no idea about how(and where, in code) to implement it.
Thanks.
I can't tell looking at the code. My guess is a variable-timestep, causing instability. But I can't verify if the math is right. Although, I have useful information :
Vectors
You can simplify code, by using vectors as a class vs list/tuple. (velocity, acceleration, location) are treated as one object, verses separate .x and .y values.
# example:
pos[0] += vel[0]
pos[1] += vel[1]
# vs
pos += vel
There is a python-only implementation: euclid.py You can use to compare with your vector.py.
Or use NumPy [ used for 3d graphics, in openGL. ] Is a popular, mature lib.
physics
(It looks like you want to learn by writing your own physics), but check out PyMunk
colors
You can use: pygame.Color
import pygame
from pygame import Color
color = Color('white')
color2 = Color('lightgray')
color3 = Color(0,128,128)
collisions
Look at pygame.sprite.*collide , and pygame.Rect.*collide
pygame Game loop with numpy vector's
Boilerplate I wrote
""" Pygame boilerplate. <ninmonkey>2011/04
pygame main Game() loop, and numpy for vector math.
note:
this might not be the most effecient way to use numpy as vectors, but it's an intro.
And this does not force fixed-timesteps. If you want a stable simulation, you need to use a fixed timestep.
see: http://gafferongames.com/game-physics/fix-your-timestep/
Keys:
ESC : exit
Space : game_init()
"""
import pygame
from pygame.locals import *
from pygame import Color, Rect
import numpy as np
def get_screen_size():
"""return screen (width, height) tuple"""
screen = pygame.display.get_surface()
return screen.get_size()
class Actor():
"""basic actor, moves randomly.
members:
loc = position vector
velocity = velocity vector
width, height
"""
def __init__(self, loc=None, velocity=None):
"""optional initial loc and velocity vectors"""
self.width = 50
self.height = 50
# if loc or velocity are not set: use random
if loc is None: self.rand_loc()
else: self.loc = loc
if velocity is None: self.rand_velocity()
else: self.velocity = velocity
def update(self):
"""update movement"""
self.loc += self.velocity
def rand_velocity(self):
"""set a random vector , based on random direction. Using unit circle:
x = cos(deg) * speed
"""
rad = np.radians( np.random.randint(0,360) )
speed = np.random.randint(1,15)
x = np.cos(rad)
y = np.sin(rad)
velocity = np.array( [x,y])
velocity *= speed
self.velocity = velocity
def rand_loc(self):
"""random location onscreen"""
width,height = get_screen_size()
x = np.random.randint(0,width)
y = np.random.randint(0,height)
self.loc = np.array([x,y])
def is_onscreen(self):
"""test is screen.colliderect(actor) true?"""
x,y = self.loc
w,h = get_screen_size()
screen = Rect(0, 0, w, h)
actor = Rect(x, y, self.width, self.height)
if screen.colliderect(actor): return True
else: return False
class GameMain():
"""game Main entry point. handles intialization of game and graphics."""
done = False
debug = False
color_gray = Color('lightgray')
def __init__(self, width=800, height=600, color_bg=None):
"""Initialize PyGame"""
pygame.init()
self.width, self.height = width, height
self.screen = pygame.display.set_mode(( self.width, self.height ))
pygame.display.set_caption( "boilerplate : pygame" )
self.clock = pygame.time.Clock()
self.limit_fps = True
self.limit_fps_max = 60
if color_bg is None: color_bg = Color(50,50,50)
self.color_bg = color_bg
self.game_init()
def game_init(self):
"""new game/round"""
self.actors = [Actor() for x in range(10)]
def loop(self):
"""Game() main loop"""
while not self.done:
self.handle_events()
self.update()
self.draw()
if self.limit_fps: self.clock.tick( self.limit_fps_max )
else: self.clock.tick()
def update(self):
"""update actors, handle physics"""
for a in self.actors:
a.update()
if not a.is_onscreen():
a.rand_loc()
def handle_events(self):
"""handle regular events. """
events = pygame.event.get()
# kmods = pygame.key.get_mods() # key modifiers
for event in events:
if event.type == pygame.QUIT: sys.exit()
elif event.type == KEYDOWN:
if (event.key == K_ESCAPE): self.done = True
elif (event.key == K_SPACE): self.game_init()
def draw(self):
"""render screen"""
# clear screen
self.screen.fill( self.color_bg )
# Actor: draw
for a in self.actors:
x,y = a.loc
w,h = a.width, a.height
r = Rect(x, y, w, h)
self.screen.fill(self.color_gray, r)
# will call update on whole screen Or flip buffer.
pygame.display.flip()
if __name__ == '__main__':
g = GameMain()
g.loop()