Having trouble with a "NameError" - python

I'm new to classes, and this is my third attempt at making one. I've ran into a NameError which I really have no idea how to solve. Take a look at my program and see if you can help.
import random
import math
import pygame
import pickle
# initialise pygame
pygame.init()
# player class
class player(object):
def __init__(self, playerimage, playerX, playerY = 700, playerX_change = 0):
self.playerimage = pygame.image.load("Main Player.png")
self.playerX = 365
self.playerY = 700
self.playerX_change = 0
# laser class
# ready - bullet not on screen
# fire - bullet is shown on screen and is moving
class laser(object):
def __init__(self, laserimage, laserX, laserY, laserY_change):
self.laserimage = pygame.image.load("laser.png")
self.laserX = 0
self.laserY = 700
self.laserY_change = 10
self.laser_state = "ready"
# alien player / random movement = random.randint()
class alien(object):
def __init__(self, alienimage, alienX, alienY, alienX_change, alienY_change, amount_aliens):
self.alienimage = pygame.image.load("alien.png")
self.alienX = []
self.alienY = []
self.alienX_change = []
self.alienY_change = []
self.amount_aliens = 10
for i in range(ufo.amount_aliens):
ufo.alienimage.append(pygame.image.load('alien.png'))
ufo.alienX.append(random.randint(0, 735))
ufo.alienY.append(random.randint(50, 200))
ufo.alienX_change.append(3)
ufo.alienY_change.append(7)
score = 0
# define player
def main_player(x, y):
screen.blit(male.playerimage, (x, y))
# define laster
def fire_laser(x, y):
lasr.laser_state = "fire"
screen.blit(lasr.laserimage, (x + 16, y + 10))
# define alien
def alien(x, y, i):
screen.blit(ufo.alienimage[i], (x, y))
# collision detection
def hascollision(alienX, alienY, laserX, laserY):
distance = math.sqrt((math.pow(alienX - laserX, 2)) + (math.pow(alienY - laserY, 2)))
if distance < 27:
return True
else:
return False
#frames per second
clock = pygame.time.Clock()
# background
background = pygame.image.load('stars.png')
# display and screen title/icon
(width, height) = (800, 800)
screen = pygame.display.set_mode((width, height))
flip = pygame.display.flip()
pygame.display.set_caption("space fighters")
pygame.event.get()
icon = pygame.image.load('logo.png')
pygame.display.set_icon(icon)
from sys import exit
ufo = alien()
lasr = laser(0, 700, 32, 32)
male = player(365, 700,)
# loop of functions
executed = True
while executed:
screen.fill((63, 62, 63))
# image background
screen.blit(background, (0, 0))
for event in pygame.event.get():
# if key pressed, check which input, right or left?
if event.type == pygame.KEYDOWN:
print("key pressed")
if event.key == pygame.K_a:
male.playerX_change = -6
if event.key == pygame.K_s:
male.playerX_change = 6
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_s:
male.playerX_change = 0
if event.key == pygame.K_SPACE:
if lasr.laser_state is "ready":
lasr.laserX = male.playerX
fire_laser(lasr.laserX, lasr.laserY)
#frames per second is 60fps
clock.tick(60)
# bounrary algorithm, prevents player moving out/enemy.
male.playerX += male.playerX_change
if male.playerX <= 0:
male.playerX = 0
elif male.playerX >= 736:
male.playerX = 736
# boundry algorithm, make sure alien doesn't go out of bountry
for i in range(ufo.amount_aliens):
ufo.alienX[i] += ufo.alienX_change[i]
if ufo.alienX[i] <= 0:
ufo.alienX_change[i] = 4
ufo.alienY[i] += ufo.alienY_change[i]
elif ufo.alienX[i] >= 736:
ufo.alienX_change[i] = -4
ufo.alienY[i] += ufo.alienY_change[i]
# collision
collision = hascollision(ufo.alienX[i], ufo.alienY[i], lasr.laserX, lasr.laserY)
if collision:
lasr.laserY = 650
lasr.laser_state = "ready"
score += 5
print(score)
alienX[i] = random.randint(0, 735)
alienY[i] = random.randint(50, 200)
alien(ufo.alienX[i], ufo.alienY[i], i)
# movement of laser shot
if lasr.laserY <= 0:
lasr.laserY = 650
lasr.laser_state = "ready"
if lasr.laser_state is "fire":
fire_laser(lasr.laserX, lasr.laserY)
lasr.laserY -= lasr.laserY_change
# updates screen to show screen
main_player(male.playerX, male.playerY)
pygame.display.update()
pygame.quit()
This is the output of the error given by visual studio code (it is on line 39)
for i in range(ufo.amount_aliens):
NameError: name 'ufo' is not defined

The alien class is a bit mixed up. (Although it's hard to tell if this is just an indentation issue in the SO paste.) I'm going to assume that the list of ufos needs to be made outside the class, because this is the only thing that makes sense. Later on in the code, you declare an alien function which will occlude the alien class too. You will need to fix this first - it's best moved into the alien class as alien.draw()
So to make a bunch of aliens, create a list:
alien_image = pygame.image.load('alien.png')
all_ufos = []
for i in range( amount_aliens ):
x_pos = random.randint( 0, 735 )
y_pos = random.randint( 50, 200 )
x_speed = 3
y_speed = 7
all_ufos.append( alien( alien_image, x_pos, y_pos, x_speed, y_speed ) )
Remove the amount_aliens from the alien object, so that it now only represents a single alien.
class alien( object ):
def __init__( self, alienimage, alienX, alienY, alienX_change, alienY_change ):
self.alienimage = alienimage
self.alienX = alienX
self.alienY = alienY
self.alienX_change = alienX_change
self.alienY_change = alienY_change
And move the support functions into the alien class.
def draw( self, screen ):
""" Draw the alien to the screen """
screen.blit( self.alienimage, ( self.alienX, self.alienY ) )
def hasCollision( self, laserX, laserY ):
""" Has the laser at collided with this alien? """
distance = math.sqrt((math.pow(self.alienX - laserX, 2)) + (math.pow(self.alienY - laserY, 2)))
return ( distance < 27 ):
This allows your main loop to iterate over the list of aliens, doing stuff simply:
### Main Loop
while not exiting:
...
# paint all UFO sprites
for ufo in all_ufos:
ufo.draw( screen )
# check all lasers for collision
for laser in all_lasers:
for ufo in all_ufos:
if ( ufo.hasCollision( laser.laserX, laser.laserY ) ):
print( "*boom*" )
What you are doing here is re-creating some of the functionality of the PyGame Sprite and SpriteGroup Classes. It might be worth a quick read of the documentation on it.

Related

Pygame everytime I add a rectangle it offsets it [duplicate]

import pygame
import sys
from pygame.sprite import Sprite
class Settings:
def __init__(self):
self.raindrop_speed = 3
self.raindrop_direction = -1
self.raindrop_dropseed = 3
self.backgroundcolor = (30,30,30)
class Drop(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("raindrop.png")
self.rect = self.image.get_rect()
self.rect.y = self.rect.height
self.rect.x = self.rect.width
self.x_cord = self.rect.x
self.y_cord = self.rect.y
class RainDrop:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1200,800))
self.settings = Settings()
self.backgroundcolor = self.settings.backgroundcolor
self.raindrops = pygame.sprite.Group()
self.screen_rect = self.screen.get_rect()
self._add_raindrops()
def _add_raindrops(self):
new_raindrop = Drop()
drop_height = new_raindrop.rect.height
drop_width = new_raindrop.rect.width
print(drop_width)
screen_space = self.screen_rect.width
screen_height_space = self.screen_rect.height
aviable_row_space = screen_height_space//(drop_height*2)
avivable_screen_space = screen_space - (drop_width*2)
amount_of_columns = avivable_screen_space//(drop_width*2)
self._add_columns(amount_of_columns,aviable_row_space)
def _add_columns(self,amount_of_columns,aviable_row_space):
for height_of_drops in range(aviable_row_space):
for number_of_drops in range(amount_of_columns):
drop = Drop()
drop.x_cord = (drop.rect.width *2)* number_of_drops
drop.y_cord =(drop.rect.height *2)* height_of_drops
drop.rect.x = drop.x_cord
drop.rect.y = drop.y_cord
self.raindrops.add(drop)
def _bring_down_raindrops(self):
for drop in self.raindrops:
drop.y_cord += self.settings.raindrop_dropseed
drop.rect.y = drop.y_cord
def _update_drops(self):
height_counter = 1
self.raindrops.update()
for drop in self.raindrops.copy():
drop.x_cord += self.settings.raindrop_direction * self.settings.raindrop_speed
drop.rect.x = drop.x_cord
if drop.rect.right >= self.screen_rect.right:
self.settings.raindrop_direction = -1
self._bring_down_raindrops()
elif drop.rect.left <= 0:
self.settings.raindrop_direction = 1
self._bring_down_raindrops()
#if drop.rect.y >= self.screen_rect.height or drop.rect.y <= 0:
# drop.rect.x = drop.rect.width
# drop.y_cord = drop.rect.height
# drop.rect.y = drop.y_cord
print(height_counter)
print(self.raindrops)
def _update_game(self):
self.screen.fill(self.backgroundcolor)
self.raindrops.draw(self.screen)
pygame.display.flip()
def _check_events(self):
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
def run_game(self):
while True:
self._check_events()
self._update_drops()
self._update_game()
if __name__ == "__main__":
rd = RainDrop()
rd.run_game()
Everytime I run this program the rectangle(waterdrops) when they reach the end of the screen a bigger gap appears I have looked at the code for several hours and I can not see what the problem is, I think it has to do with the condition in bottom but I am not sure I have messed with it and changed some values but it still does the same thing.
Your problem is in what you do when you recognize that you've hit the edge of the screen. You do this when you find any drop that has gone off of the screen, and at that point, you reverse direction. The problem is, you've already moved some of the drops on the top row in one direction, but after you recognize the edge of the screen, you then go on to move the rest of the drops in the opposite direction in that same pass. This is how things get out of wack.
What you want to do is note that you've hit the edge of the screen, but not do anything differently right away, so that you still deal with all of the drops on the screen the same way in that pass. After you've drawn all of the drops, you then change direction.
Here's a version of _update_drops that will do this:
def _update_drops(self):
height_counter = 1
self.raindrops.update()
# assume we haven't hit either edge of the screen
reverse = 0
for drop in self.raindrops.copy():
drop.x_cord += self.settings.raindrop_direction * self.settings.raindrop_speed
drop.rect.x = drop.x_cord
if drop.rect.right >= self.screen_rect.right:
# remember that we hit the right edge of the screen
reverse = -1
elif drop.rect.left <= 0:
# remember that we hit the left edge of the screen
reverse = 1
# if we hit one of the edges, change directions and drop down
if reverse != 0:
self.settings.raindrop_direction = reverse
self._bring_down_raindrops()

How do I find the distance from the goal node named target, when I have the below mentioned start node named seeker?

I am trying to find the distance from the start node named seeker to the goal node named target. I have already done most of the implementation of the algorithm and each of the nodes have a coordinate or box on a grid. My python code is lacking and I cannot find the coordinates and distance.
I have tried using the vector in the math class which gave me logical errors.
I also tried sending the actual objects hoping I could use those to reference the coordinates as I'd done in the code but got a TypeError: expected sequence object with len >= 0 or a single integer.
import numpy as np
import pygame as pg
import sys
from Settings import *
from Sprites import *
vec = pg.math.Vector2
class Game:
# initialise game window, etc
def __init__(self):
self.playing = True
pg.init()
self.mover = 'target'
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(Title)
self.clock = pg.time.Clock()
pg.key.set_repeat(500, 100)
self.load_data()
# pg.mouse.set_pos(0, 0)
self.walls = []
def load_data(self):
pass
# start a new game
def new(self):
self.all_sprites = pg.sprite.Group()
self.player = Player(self, 10, 10)
self.target = Target(self, 5, 5)
def load_data(self):
pass
# game loop
def run(self):
while self.playing:
self.dt = self.clock.tick(FPS) / 10000
self.events()
self.update()
self.draw()
def draw_grid(self):
for x in range(0, WIDTH, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (0, y), (WIDTH, y))
def quit(self):
pg.quit()
sys.exit()
def update(self):
# game loop update
self.all_sprites.update()
def events(self):
# game loop events
for event in pg.event.get():
if event.type == pg.QUIT:
self.quit()
if event.type == pg.MOUSEBUTTONDOWN:
mpos = vec(pg.mouse.get_pos()) // TILESIZE
if event.button == 1:
if mpos in g.walls:
g.walls.remove(mpos)
else:
g.walls.append(mpos)
if event.type == pg.KEYDOWN:
if event.key == pg.K_b:
# start breadth first
breadthfirst(self.target, self.player, self.walls, self.screen)
if event.key == pg.K_d:
# start depth first
depthfirst(self.target, self.player, self.walls, self.screen)
if event.key == pg.K_a:
# start A* search
astarsearch(self.target, self.player, self.walls, self.screen)
if event.key == pg.K_ESCAPE:
self.quit()
if event.key == pg.K_t:
self.mover = 'target'
if event.key == pg.K_s:
self.mover = 'seeker'
if self.mover == 'target':
if event.key == pg.K_LEFT:
self.target.move(dx=-1)
if event.key == pg.K_RIGHT:
self.target.move(dx=1)
if event.key == pg.K_UP:
self.target.move(dy=-1)
if event.key == pg.K_DOWN:
self.target.move(dy=1)
else:
if event.key == pg.K_LEFT:
self.player.move(dx=-1)
if event.key == pg.K_RIGHT:
self.player.move(dx=1)
if event.key == pg.K_UP:
self.player.move(dy=-1)
if event.key == pg.K_DOWN:
self.player.move(dy=1)
def draw(self):
# game loop - draw
self.screen.fill(BGCOLOR)
self.draw_grid()
self.all_sprites.draw(self.screen)
for wall in self.walls:
rect = pg.Rect(wall * TILESIZE, (TILESIZE, TILESIZE))
pg.draw.rect(self.screen, GREEN, rect)
# always do the flip after drawing everything
pg.display.flip()
def show_start_screen(self):
# game splash/start screen
pass
def show_go_screen(self):
# game over/continue screen
pass
def breadthfirst(target, seeker, walls, maingrid):
pass
def depthfirst(target, seeker, wall, maingrid):
pass
def astarsearch(target, seeker, wall, maingrid):
# array to store path locations
direct_graph = {}
# target location, returns a tuple
target = np.where(target.pos) # problem area
# seeker location, returns locations
start = np.where(seeker.pos) # problem area
# array of cost to travel so far
g_cost_array = np.zeros(seeker) # problem area
g_cost_array[start] = 0
total_g_cost = 0
# array for heuristic cost
h_cost_array = np.zeros(seeker) # problem area
# need to use a loop unfortunately...
t = 0
# possible steps
steps = ((-1, 0), (+1, 0), (0, -1), (0, +1))
for rows in h_cost_array:
s = 0
for cols in rows:
# check if it's a wall! if not - get the distance to target
loc = (t, s)
if (maingrid[loc]):
pass
else:
dist = abs(target[0] - s) + abs(target[1] - t)
h_cost_array[t, s] = dist
s += 1
t += 1
# total cost = h + g
f_cost_array = g_cost_array + h_cost_array
# closed and open sets
open_set = []
open_set.append(start)
closed_set = []
# actual path
path = []
path.append([tuple(target[0]), tuple(target[1])])
solution_found = False
while (open_set):
open_f_cost = []
# get the heuristic cost for the candidates in the open set
for vals in open_set:
open_f_cost.append(f_cost_array[vals])
# the shortest heuristic now
# the index of the candidate with the lowest distance/heuristic
best_dist_now = open_f_cost.index(min(open_f_cost))
# the current best position
best_pos_now = open_set[best_dist_now]
# if the destination is reached, finish!
if (tuple(best_pos_now) == target):
solution_found = True
break
else:
# remove the best guy from the open_set and add it to the closed set
closed_set.append(open_set.pop(best_dist_now))
# analyze the steps from the current best
for step in steps:
cand = (best_pos_now[0] + step[0], best_pos_now[1] + step[1])
# check if there's a wall or beyond the screen
if cand[0] < 0 or cand[1] < 0 or cand[0] > 39 or cand[1] > 23:
pass
# skip this candidate because it's a wall!
elif maingrid[cand]:
pass
# need an else clause here to weed out the off-screen locations
else:
# check if the candidate is in the closed set
already_seen = False
for dead in closed_set:
if np.all(dead == cand):
already_seen = True
break
# if the cell is in the closed set, skip it
if already_seen:
pass
else:
approx_g_score = g_cost_array[best_pos_now] + 1
# check if it's in the open list:
new = True
for others in open_set:
if np.all(others == cand):
new = False
break
# if a new cell or improved
if (new or approx_g_score < g_cost_array[cand]):
direct_graph[tuple(cand[0]), tuple(cand[1])] = (
tuple(best_pos_now[0]), tuple(best_pos_now[1]))
g_cost_array[cand] = approx_g_score
f_cost_array[cand] = g_cost_array[cand] + h_cost_array[cand]
if new:
open_set.append(cand)
if not solution_found:
return None
else:
recurrentPath(path, direct_graph, target, start)
return path
# takes a dictionary as input
def recurrentPath(final_path, raw_path, dest, origin):
a = raw_path[tuple(dest[0]), tuple(dest[1])]
final_path.append(a)
if (a != origin):
recurrentPath(final_path, raw_path, a, origin)
else:
return final_path
g = Game()
g.show_start_screen()
while True:
g.new()
g.run()
g.show_go_screen()
pg.QUIT
the expected results is to return the correct path to the target object from the seeker object after you press the keyboard character a. Note that with a left click, a wall can be constructed to add a hindrance to the seeking of the goal.

Python game keys are not responding

I am trying to implement a Python game (aliens.py from the PyGame package) but when I run it, I can't move my player or do any shooting...
I run it on Mac OS X, python3 and have a french keyboard. Could this have anything to do the fact that it does not take any of my keyboard commands?
On the screen of my terminal I see:
^[ (when I press esc),
^[[D (when I press the left arrow),
^[[C (when I press the right arrow),
^[[A (when I press the upwards arrow),
^[[B (when I press the downwards arrow
...
Is this normal? It does not help replacing K_RIGHT with ^[[C.
#!/usr/bin/env python
import random, os.path
#import basic pygame modules
import pygame
from pygame.locals import *
#see if we can load more than standard BMP
if not pygame.image.get_extended():
raise SystemExit("Sorry, extended image module required")
#game constants
MAX_SHOTS = 2 #most player bullets onscreen
ALIEN_ODDS = 22 #ances a new alien appears
BOMB_ODDS = 60 #chances a new bomb will drop
ALIEN_RELOAD = 12 #frames between new aliens
SCREENRECT = Rect(0, 0, 940, 480)
SCORE = 0
main_dir = os.path.split(os.path.abspath(__file__))[0]
def load_image(file):
"loads an image, prepares it for play"
file = os.path.join(main_dir, 'data', file)
try:
surface = pygame.image.load(file)
except pygame.error:
raise SystemExit('Could not load image "%s" %s'%(file, pygame.get_error()))
return surface.convert()
def load_images(*files):
imgs = []
for file in files:
imgs.append(load_image(file))
return imgs
class dummysound:
def play(self): pass
def load_sound(file):
if not pygame.mixer: return dummysound()
file = os.path.join(main_dir, 'data', file)
try:
sound = pygame.mixer.Sound(file)
return sound
except pygame.error:
print ('Warning, unable to load, %s' % file)
return dummysound()
# each type of game object gets an init and an
# update function. the update function is called
# once per frame, and it is when each object should
# change it's current position and state. the Player
# object actually gets a "move" function instead of
# update, since it is passed extra information about
# the keyboard
class Player(pygame.sprite.Sprite):
speed = 10
bounce = 24
gun_offset = -11
images = []
def __init__(self):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
self.reloading = 0
self.origtop = self.rect.top
self.facing = -1
def move(self, direction):
if direction: self.facing = direction
self.rect.move_ip(direction*self.speed, 0)
self.rect = self.rect.clamp(SCREENRECT)
if direction < 0:
self.image = self.images[0]
elif direction > 0:
self.image = self.images[1]
self.rect.top = self.origtop - (self.rect.left//self.bounce%2)
def gunpos(self):
pos = self.facing*self.gun_offset + self.rect.centerx
return pos, self.rect.top
class Alien(pygame.sprite.Sprite):
speed = 13
animcycle = 12
images = []
def __init__(self):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.facing = random.choice((-1,1)) * Alien.speed
self.frame = 0
if self.facing < 0:
self.rect.right = SCREENRECT.right
def update(self):
self.rect.move_ip(self.facing, 0)
if not SCREENRECT.contains(self.rect):
self.facing = -self.facing;
self.rect.top = self.rect.bottom + 1
self.rect = self.rect.clamp(SCREENRECT)
self.frame = self.frame + 1
self.image = self.images[self.frame//self.animcycle%3]
class Explosion(pygame.sprite.Sprite):
defaultlife = 12
animcycle = 3
images = []
def __init__(self, actor):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(center=actor.rect.center)
self.life = self.defaultlife
def update(self):
self.life = self.life - 1
self.image = self.images[self.life//self.animcycle%2]
if self.life <= 0: self.kill()
class Shot(pygame.sprite.Sprite):
speed = -11
images = []
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=pos)
def update(self):
self.rect.move_ip(0, self.speed)
if self.rect.top <= 0:
self.kill()
class Bomb(pygame.sprite.Sprite):
speed = 9
images = []
def __init__(self, alien):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=
alien.rect.move(0,5).midbottom)
def update(self):
self.rect.move_ip(0, self.speed)
if self.rect.bottom >= 470:
Explosion(self)
self.kill()
class Score(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.font = pygame.font.Font(None, 20)
self.font.set_italic(1)
self.color = Color('white')
self.lastscore = -1
self.update()
self.rect = self.image.get_rect().move(10, 450)
def update(self):
if SCORE != self.lastscore:
self.lastscore = SCORE
msg = "Score: %d" % SCORE
self.image = self.font.render(msg, 0, self.color)
def main(winstyle = 0):
# Initialize pygame
pygame.init()
if pygame.mixer and not pygame.mixer.get_init():
print ('Warning, no sound')
pygame.mixer = None
# Set the display mode
winstyle = 0 # |FULLSCREEN
bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32)
screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth)
#Load images, assign to sprite classes
#(do this before the classes are used, after screen setup)
img = load_image('player1.gif')
Player.images = [img, pygame.transform.flip(img, 1, 0)]
img = load_image('explosion1.gif')
Explosion.images = [img, pygame.transform.flip(img, 1, 1)]
Alien.images = load_images('alien1.gif', 'alien2.gif', 'alien3.gif')
Bomb.images = [load_image('bomb.gif')]
Shot.images = [load_image('shot.gif')]
#decorate the game window
icon = pygame.transform.scale(Alien.images[0], (32, 32))
pygame.display.set_icon(icon)
pygame.display.set_caption('Pygame Aliens')
pygame.mouse.set_visible(0)
#create the background, tile the bgd image
bgdtile = load_image('background.gif')
background = pygame.Surface(SCREENRECT.size)
for x in range(0, SCREENRECT.width, bgdtile.get_width()):
background.blit(bgdtile, (x, 0))
screen.blit(background, (0,0))
pygame.display.flip()
#load the sound effects
boom_sound = load_sound('boom.wav')
shoot_sound = load_sound('car_door.wav')
if pygame.mixer:
music = os.path.join(main_dir, 'data', 'house_lo.wav')
pygame.mixer.music.load(music)
pygame.mixer.music.play(-1)
# Initialize Game Groups
aliens = pygame.sprite.Group()
shots = pygame.sprite.Group()
bombs = pygame.sprite.Group()
all = pygame.sprite.RenderUpdates()
lastalien = pygame.sprite.GroupSingle()
#assign default groups to each sprite class
Player.containers = all
Alien.containers = aliens, all, lastalien
Shot.containers = shots, all
Bomb.containers = bombs, all
Explosion.containers = all
Score.containers = all
#Create Some Starting Values
global score
alienreload = ALIEN_RELOAD
kills = 0
clock = pygame.time.Clock()
#initialize our starting sprites
global SCORE
player = Player()
Alien() #note, this 'lives' because it goes into a sprite group
if pygame.font:
all.add(Score())
while player.alive():
#get input
for event in pygame.event.get():
if event.type == QUIT or \
(event.type == KEYDOWN and event.key == K_ESCAPE):
return
keystate = pygame.key.get_pressed()
# clear/erase the last drawn sprites
all.clear(screen, background)
#update all the sprites
all.update()
#handle player input
direction = keystate[K_RIGHT] - keystate[K_LEFT]
player.move(direction)
firing = keystate[K_SPACE]
if not player.reloading and firing and len(shots) < MAX_SHOTS:
Shot(player.gunpos())
shoot_sound.play()
player.reloading = firing
# Create new alien
if alienreload:
alienreload = alienreload - 1
elif not int(random.random() * ALIEN_ODDS):
Alien()
alienreload = ALIEN_RELOAD
# Drop bombs
if lastalien and not int(random.random() * BOMB_ODDS):
Bomb(lastalien.sprite)
# Detect collisions
for alien in pygame.sprite.spritecollide(player, aliens, 1):
boom_sound.play()
Explosion(alien)
Explosion(player)
SCORE = SCORE + 1
player.kill()
for alien in pygame.sprite.groupcollide(shots, aliens, 1, 1).keys():
boom_sound.play()
Explosion(alien)
SCORE = SCORE + 1
for bomb in pygame.sprite.spritecollide(player, bombs, 1):
boom_sound.play()
Explosion(player)
Explosion(bomb)
player.kill()
#draw the scene
dirty = all.draw(screen)
pygame.display.update(dirty)
#cap the framerate
clock.tick(40)
if pygame.mixer:
pygame.mixer.music.fadeout(1000)
pygame.time.wait(1000)
pygame.quit()
#call the "main" function if running this script
if __name__ == '__main__': main()
Event KEYDOWN sends values unicode, key, mod and you can display it to see what codes (numeric values) uses your keyboard.
if event.type == KEYDOWN;
print('key:', event.key)
print('unicode:', event.uniconde)
print('mod:', event.mod)
And then you can use them to test your keys and move objects.
It is surely due to keyboard keys problem , just go to this documentation and find out proper keyword for your respective keyboard button:
"https://www.pygame.org/docs/ref/key.html"

How to make a mouse event for clicks on a masked image [Pygame]

So I'm really new to programming and I'm working on a game that is sort of like Cookie Clicker, but with a twist (Mining). It is made in python/pygame. Anyways I have an image of a boulder and I want to add a rock to my inventory in the game every time i click on it.
Someone helped me out setting up the point_collide in my class. I will confess and say I don't fully understand how it works but its supposed to detect if my mouse is on a non-transparent part of my rock image.
I want the game to only give you a rock if your clicking on the NON-transparent parts of the boulder image I have blitted to the middle of the screen.
Short Question: How can I setup the game to register clicks only on my masked image?
PS: I know it'd be better to learn the fundamentals of programming first, but I have learned so much just by diving straight into a project (it keeps me going and alot more fun then reading a book).
Link to code: https://www.refheap.com/88634
The Code:
import pygame, sys
from pygame.locals import *
from datetime import datetime
if (__name__ == "__main__"):
pygame.init()
pygame.font.init()
pygame.display.set_caption("Miner Click")
clock = pygame.time.Clock()
screen = pygame.display.set_mode((960,600))
width = 960
height = 600
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)
BROWN = (84,27,1)
GREY = (198,198,198)
greenbg = pygame.image.load("greenbg.jpg").convert()
rockbutton = pygame.image.load("rockbutton.png").convert_alpha()
woodbutton = pygame.image.load("woodbutton.png").convert_alpha()
pygame.mouse.set_visible(True)
pick = pygame.image.load("pick.png").convert_alpha()
axe = pygame.image.load("axesmall.png").convert_alpha()
rockwidth = 544
rockheight = 274
clicks = 0
wood = 0
stonefont = pygame.font.SysFont("verdana", 29, True)
woodfont = pygame.font.SysFont("verdana", 29, True)
clicktext = stonefont.render('Rock: ' +str(clicks), 2, (GREY))
woodtext = woodfont.render('Wood: ' +str(wood), 2, (BROWN))
boxsize = clicktext.get_rect()
RocksX = 125
WoodX = 113
class Rock(pygame.sprite.Sprite):
def __init__(self, color = BLUE, width = 544, height = 274):
super(Rock, self ).__init__()
self.image = pygame.Surface((width, height))
self.set_properties()
self.image.fill(color)
def set_properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_position(self, x, y):
self.rect.x = 250
self.rect.y = 230
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
try:
return self.mask.get_at((x,y))
except IndexError:
return False
#below is my clueless attempt at getting it to work
def checkForCursorPressed(x,y):
if pygame.mouse.get_pressed() and pygame.mouse.get_pos() == (x,y):
clicks+=1
coordfont = pygame.font.SysFont("verdana", 12, True)
rock_group = pygame.sprite.Group()
rock = Rock()
rock.set_image("rock.png")
rock.set_position(width/2, height/2)
rock_group.add(rock)
while True:
clock.tick(60)
screen.fill((255,255,255))
screen.blit(greenbg, (0,0))
x,y = pygame.mouse.get_pos()
coords = x,y
now = datetime.now()
date = '%s/%s/%s' % (now.month, now.day, now.year)
label = coordfont.render("Coordinates: "+str(coords), 1, (GREY))
date = coordfont.render("Date: "+str(date), 1, (GREY))
screen.blit(date, (650,10))
screen.blit(label, (790, 10))
screen.blit(rockbutton, (25,25))
screen.blit(woodbutton, (25,100))
clicktext = stonefont.render(' ' +str(clicks), 2, (GREY))
woodtext = woodfont.render(' ' +str(wood), 2, (BROWN))
screen.blit(clicktext, [RocksX,38])
screen.blit(woodtext, [139,WoodX])
rock_group.draw(screen)
screen.blit(pick, (x-10,y-50))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
pygame.display.update()
#in case i need the below again
#if x>249 and x<(795) and y>210 and y<(484):
Clicking will create a MOUSEBUTTONDOWN event, so you should be able to deal with clicks within your event-processing loop, e.g.:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
click_position = event.pos
if rock.point_collide(click_position):
print('Clicked within the rock')
clicks += 1
# Any other events that have to happen
# when the rock is clicked
I also don't understand how point_collide() works and I get an AttributeError: 'Rock' does not have attribute 'mask'.
So I would use an other possibility to detect if one clicked on non-transparent parts of the image, using colorkey.
The colorkey defines the transparent color when blitting. In my case it's white:
def set_image(self, filename = None):
...
#sets colorkey to white, depends on the image
self.image.set_colorkey((255,255,255))
New version of point_collide():
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
#detects if click hits the image
if 0 <= x < self.image.get_width():
if 0 <= y < self.image.get_height():
#detects if color at clicking position != colorkey-color(transparent)
if self.image.get_at((x,y))[0:3] != self.image.get_colorkey()[0:3]:
return True
return False
How to get mouse events has been answered yet.
Ok I got it working :)
If anyone else is trying to do something similar I'll explain here.
Put the below in your code (this solves the problem 'Rock has no attribute mask' error I got)
self.mask = pygame.mask.from_surface(self.image)
Here is where I put it in my code (in my set_image def)
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
self.mask = pygame.mask.from_surface(self.image)
Now combine this code with Marius and it works perfectly!

What do i call if its in a special method

I'm trying to make a function when the mouse button is up, it changes the picture of the ghosts to one single image.
Problem being, I have no idea what to call upon (Hence the ??? in the script). It's hard because the ghosts are created via a loop. Can anyone possibly help?
Maybe i need to change the ghosts into sprites? could you help with that too?
import pygame
import random
import sys
class Ball:
def __init__(self,X,Y,imagefile):
self.velocity = [3,3]
self.ball_image = pygame.image.load (imagefile). convert() ### i want this image to change
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
if __name__ =='__main__':
width = 800
height = 600
background_colour = 0,0,0
GHOST_IMAGE = ["images/blue-right.png", "images/red-right.png", "images/orange-right.png", "images/pink-right.png"]
GHOST_IMAGETWO = ["images/blue-left.png", "images/red-left.png", "images/orange-left.png", "images/pink-left.png"]
pygame.init()
frame = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bouncing Ball animation")
num_balls = 4
ball_list = []
for i in range(num_balls):
ball_list.append( Ball(random.randint(0, width),random.randint(0, height), (GHOST_IMAGE[i]) ))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.MOUSEBUTTONUP:
??? = pygame.image.load("images/vulnerable.png").convert() ###i know that this is where and what i need to change it to, but dont know what instance name to call upon.
frame.fill(background_colour)
for ball in ball_list:
if ball.ball_boundary.left < 0 or ball.ball_boundary.right > width:
ball.sound.play()
ball.velocity[0] = -1 * ball.velocity[0]
if ball.ball_boundary.top < 0 or ball.ball_boundary.bottom > height:
ball.sound.play()
ball.velocity[1] = -1 * ball.velocity[1]
ball.ball_boundary = ball.ball_boundary.move (ball.velocity)
frame.blit (ball.ball_image, ball.ball_boundary)
pygame.display.flip()
One way is to just iterate over the ball_list and change each ball:
elif event.type == pygame.MOUSEBUTTONUP:
image = pygame.image.load("images/vulnerable.png").convert()
for ball in ball_list:
ball.ball_image = image
Another way would be implement the image-changing behaviour directly in the Ball class:
class Ball:
def __init__(self,X,Y,imagefile):
self.vulnerable = False
self.velocity = [3,3]
self.normal_ball_image = pygame.image.load (imagefile). convert()
self.v_ball_image = pygame.image.load("images/vulnerable.png").convert()
self.ball_image = self.normal_ball_image
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
def toggle(self):
self.vulnerable = not self.vulnerable
self.ball_image = self.v_ball_image if self.vulnerable else self.normal_ball_image
And in your loop:
elif event.type == pygame.MOUSEBUTTONUP:
for ball in ball_list:
ball.toggle()

Categories

Resources