I attempted to make a function that allows my sprites to re-produce but it create an infinite amount [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I made a class function that will allow the animals to reproduce once they have eaten enough of a group, from there it would create a new sprite on that location, and then it should lower the energy Val of that sprite, instead it does not do this and instead causes an infinite amount of sprites to be created causing the program to slow down and crash.
this is the functions I made for it to work
def checkEnergy(self):
EnergyVal=self.energy
return EnergyVal
def reproduce(self,energy,Xpos,Ypos):
if energy==10:
prey = Prey(prey_image,Xpos,Ypos )
all_sprites.add(prey)
Rabbit_group.add(prey)
self.energy-=4
here is where I implemented it:
if len(carrots) > 0:
for rabbit in Rabbit_group:
movementx, movementy = Track(rabbit.x, carrots[0].x, rabbit.y, carrots[0].y)#how the prey move
rabbit.move(movementx, movementy)
energyval=prey.checkEnergy()
rabbit.reproduce(energyval,600,700)
full code:
#simulates interactions of wild life
import math
import random
import pygame,sys
import random
import pdb
from pygame.locals import *
#classes--------------
class Animal(pygame.sprite.Sprite):#main class
def __init__(self, image, x, y):
super().__init__()
self.x = x
self.y = y
self.image = image
self.rect = self.image.get_rect(center = (x, y))
self.Energy=10
def move(self, mx, my):#moves the sprite
self.x += mx
self.y += my
self.rect = self.image.get_rect(center = (self.x, self.y))#sets the image to the position
def is_collided_with(self, sprite):#is the eat function
return self.rect.colliderect(sprite.rect)
def checkEnergy(self):
EnergyVal=self.energy
return EnergyVal
def reproduce(self,energy,Xpos,Ypos):
if energy==10:
prey = Prey(prey_image,Xpos,Ypos )
all_sprites.add(prey)
Rabbit_group.add(prey)
self.energy-=4
class Predator(Animal):#class for predators
def __init__(self):
image = pygame.image.load('Icon.png')
self.energy=10
super().__init__(image, 600, 700)
class Prey(Animal):
"""prey class"""
def __init__(self, image, x, y):
super().__init__(image, x, y)
self.energy=10
#self.rect = self.image.get_rect(center = (self.x, self.y))
class Carrot(pygame.sprite.Sprite):#food for prey class
def __init__(self):
super().__init__()
self.x = 100
self.y = 100
self.image = pygame.image.load('carrot.png')
self.rect = self.image.get_rect(center = (self.x, self.y))
# Functions-----------
def Track(AgressorX,DefenderX,AgressorY,DefenderY):#finds comapres X and Y, tells sprite how to move
if AgressorX > DefenderX:
XMovement=-1
elif AgressorX< DefenderX:
XMovement=1
else:
XMovement=0
if AgressorY > DefenderY:
YMovement=-1
elif AgressorY < DefenderY:
YMovement=1
else:
YMovement=0
return XMovement, YMovement
def leavingWindow(Xpos, Ypos):#boundry, keeps sprites in the window
if Xpos<=0:
return True
elif Xpos>=950:
return True
if Ypos<=0:
return True
elif Ypos>=750:
return True
def Boundry(Xpos, Ypos):
if Xpos<=0:
Xmovement=0
elif Xpos>=950:
Xmovement=950
else:
Xmovement=+0
if Ypos<=0:
ymovement=0
elif Ypos>=750:
ymovement=60
else:
ymovement=+0
return Xmovement, ymovement
pygame.init()#setsup pygame
screen = pygame.display.set_mode((1000,800))#setsup screen
clock = pygame.time.Clock()#tells program how fast to update
timmer = 1#how long program has gone on for
#setsup the classes
predator = Predator()
predator2=Predator()
#prey = Prey()
carrot = Carrot()
all_sprites = pygame.sprite.Group()
prey_image = pygame.image.load('Prey.png')
carrot_group = pygame.sprite.Group()
carrot_group.add(Carrot())
Wolf_group= pygame.sprite.Group()
Wolf_group.add(Predator())
Rabbit_group= pygame.sprite.Group()
for i in range(0, 2):
prey = Prey(prey_image, 100 * i, 700)
all_sprites.add(prey)
Rabbit_group.add(prey)
#setsup the classes as sprites
all_sprites.add(carrot_group)
all_sprites.add(Wolf_group)
all_sprites.add(Rabbit_group)
#start of program
running=True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
carrots = carrot_group.sprites()
preys = Rabbit_group.sprites()
predators = Wolf_group.sprites()
if len(preys) > 0:
for wolf in Wolf_group:
movementx, movementy = Track(wolf.x, preys[0].x, wolf.y, preys[0].y)#how the predators move
wolf.move(0,1)#movementx, #movementy)
Leaving=leavingWindow(wolf.x,wolf.y)#keeps sprites in window
if Leaving == True:
movementx,movementy=Boundry(wolf.x,wolf.y)
wolf.x=movementx
wolf.y=movementy
if len(carrots) > 0:
for rabbit in Rabbit_group:
movementx, movementy = Track(rabbit.x, carrots[0].x, rabbit.y, carrots[0].y)#how the prey move
rabbit.move(movementx, movementy)
energyval=prey.checkEnergy()
rabbit.reproduce(energyval,600,700)
screen.fill((0,128,0))#background
all_sprites.draw(screen)#makes the sprites
pygame.display.update()#updates screen
timmer=timmer+1
for carrot in carrots:#removes the carrots if they contact a rabbit
for rabbit in Rabbit_group:
if rabbit.is_collided_with(carrot):
print('works')
carrot.kill()
for wolf in Wolf_group:
for rabbit in Rabbit_group:
if rabbit.is_collided_with(wolf):
print("works 2")
rabbit.kill()
pygame.quit()
exit()
MRE Here:
import pygame,sys
import random
import pdb
from pygame.locals import *
class Animal(pygame.sprite.Sprite):#main class
def __init__(self, image, x, y):
super().__init__()
self.x = x
self.y = y
self.image = image
self.rect = self.image.get_rect(center = (x, y))
self.Energy=10
def move(self, mx, my):#moves the sprite
self.x += mx
self.y += my
self.rect = self.image.get_rect(center = (self.x, self.y))#sets the image to the position
def checkEnergy(self):
EnergyVal=self.energy
return EnergyVal
def reproduce(self,energy,Xpos,Ypos):
if energy==10:
prey = Prey(prey_image,Xpos,Ypos )
all_sprites.add(prey)
Rabbit_group.add(prey)
self.energy-=4
class Prey(Animal):
"""prey class"""
def __init__(self, image, x, y):
super().__init__(image, x, y)
self.energy=10
#self.rect = self.image.get_rect(center = (self.x, self.y))
pygame.init()#setsup pygame
screen = pygame.display.set_mode((1000,800))#setsup screen
clock = pygame.time.Clock()#tells program how fast to update
timmer = 1#how long program has gone on for
prey_image = pygame.image.load('Prey.png')
Rabbit_group= pygame.sprite.Group()
all_sprites=pygame.sprite.Group()
for i in range(0, 2):
prey = Prey(prey_image, 100 * i, 700)
all_sprites.add(prey)
Rabbit_group.add(prey)
#setsup the classes as sprites
all_sprites.add(Rabbit_group)
#start of program
running=True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
preys = Rabbit_group.sprites()
for rabbit in Rabbit_group:
rabbit.move(0, -1)
energyval=prey.checkEnergy()
rabbit.reproduce(energyval,600,700)
screen.fill((0,128,0))#background
all_sprites.draw(screen)#makes the sprites
pygame.display.update()#updates screen
timmer=timmer+1
pygame.quit()
exit()
I'm pretty sure the issue is self.energy-=4 I don't think its working properly but I am unsure on how to fix it.

I made a class function that will allow the animals to reproduce once they have eaten enough of a group [...]
You do the evaluation at the wrong place.
if len(carrots) > 0:
for rabbit in Rabbit_group:
movementx, movementy = Track(rabbit.x, carrots[0].x, rabbit.y, carrots[0].y)#how the prey move
rabbit.move(movementx, movementy)
# energyval=prey.checkEnergy() <--- DELETE
# rabbit.reproduce(energyval,600,700) <--- DELETE
Increment the energy when the rabbit eats a carrot. And reproduce the rabbit if the energy reaches the threshold:
class Animal(pygame.sprite.Sprite):#main class
# [...]
def reproduce(self):
if self.energy >= 10:
prey = Prey(self.image, self.x, self.y)
all_sprites.add(prey)
Rabbit_group.add(prey)
self.energy -= 4
while running:
# [...]
for carrot in carrots:#removes the carrots if they contact a rabbit
for rabbit in Rabbit_group:
if rabbit.is_collided_with(carrot):
print('works')
carrot.kill()
rabbit.energy += 1
rabbit.reproduce()

Related

How can I blit a random image at a random coordinate everytime collision occurs? [duplicate]

This question already has answers here:
How can i controll collision from sprites?
(1 answer)
How can I show explosion image when collision happens?
(2 answers)
Adding a particle effect to my clicker game
(1 answer)
Closed 4 months ago.
with my code right now, whenever my sprite collides with a Pokemon, the pokemon blits at random locations 3 - 5 times then just doesn't let me collide with it anymore. Sometimes the pokemon that blits is random sometimes its not.
Here is my pokemon code and my collision code:
class Pokemon:
def __init__(self, parent_screen):
self.pokemon = [
pygame.image.load('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/pokemon/bulbasaur.png').convert(),
pygame.image.load('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/pokemon/caterpie.png').convert(),
pygame.image.load('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/pokemon/charmander.png').convert(),
pygame.image.load('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/pokemon/pidgey.png').convert(),
pygame.image.load('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/pokemon/squirtle.png').convert()
]
self.parent_screen = parent_screen
self.x = SIZE
self.y = SIZE
self.random_pokemon = random.choice(self.pokemon)
self.set_colorkey()
def set_colorkey(self):
for i in range(len(self.pokemon)):
image = self.pokemon[i]
image.set_colorkey((0, 0, 0))
self.pokemon[i] = pygame.transform.scale(image, (150, 150))
def move(self):
self.parent_screen.blit(random.choice(self.pokemon), (self.x, self.y))
self.x = random.randint(1,25)*SIZE
self.y = random.randint(1,25)*SIZE
def draw_pokemon(self):
self.parent_screen.blit(self.random_pokemon, (self.x, self.y))
#MY COLLISION CODE:
score = 0
class Game:
def __init__(self):
pygame.init()
pygame.display.set_caption('Pokemon Catcher')
pygame.mixer.init()
self.surface = pygame.display.set_mode((700, 677))
self.background_music()
self.snake = Snake(self.surface)
self.snake.draw()
self.random_pokemon = Pokemon(self.surface)
self.pokemon = Pokemon(self.surface)
self.pokemon.draw_pokemon()
def collide(self,x1,y1,x2,y2):
distance = math.sqrt((math.pow(x2 - x1,2)) + (math.pow(y2-y1,2)))
if distance < 40:
return True
else:
return False
def play(self):
global score
self.render_background()
self.snake.walk()
self.pokemon.draw_pokemon()
self.display_score()
pygame.display.flip()
if self.collide(self.snake.x, self.snake.y, self.random_pokemon.x, self.random_pokemon.y):
score += 1
self.pokemon.move()
sound = pygame.mixer.Sound('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/music/cartoon eat.wav')
pygame.mixer.Sound.play(sound)
pygame.display.flip()
# collide with boundary
if self.snake.x <= -40 or self.snake.y <= -40 or self.snake.x >= 650 or self.snake.y >= 600:
sound = pygame.mixer.Sound('/Users/gersh/PycharmProjects/snakeeo/venv/lib/resources/music/bump.wav')
pygame.mixer.Sound.play(sound)
raise 'Hit the boundry error'

How to program a collision

I want to code a collision. I have 2 classes and if they collide one of them should undraw for 1 second
#Laden der Pygame Bibliothek
import pygame
import time
import random
#Initialisierung der Pygame Bibliothek
pygame.init()
# Spiel-Fenster erstellen
size = [700, 500]
screen = pygame.display.set_mode(size)
screen.fill((255,255,255))
# Noetig um die fps zu begrenzen
clock = pygame.time.Clock()
# Speichert ob das Spiel-Fenster geschlossen wurde
done = False
First class that spawn an object that only can move left and right
class Schlitten():
def __init__(self, px, py, pscreen):
self.FARBE1 = (139,87,66)
self.FARBE2 = (139,90,43)
self.braun = (104,73,71)
self.x = px
self.grau = (118,122,121)
self.y = py
self.red = (255,0,0)
self.screen = pscreen
self.hit = False
def draw(self):
if self.hit == False:
pygame.draw.rect(self.screen, self.FARBE2, [self.x,self.y,5,75])
pygame.draw.rect(self.screen, self.FARBE2, [self.x+29,self.y,5,75])
pygame.draw.rect(self.screen, self.braun, [self.x+5,self.y+20,24,3])
pygame.draw.rect(self.screen, self.braun, [self.x+5,self.y+55,24,3])
pygame.draw.rect(self.screen, self.FARBE1, [self.x+6,self.y+15,3,50])
pygame.draw.rect(self.screen, self.FARBE1, [self.x+12,self.y+15,3,50])
pygame.draw.rect(self.screen, self.FARBE1, [self.x+18,self.y+15,3,50])
pygame.draw.rect(self.screen, self.FARBE1, [self.x+24,self.y+15,3,50])
pygame.draw.rect(self.screen, self.grau, [self.x+5,self.y+10,24,2])
def kollision(self):
self.hit = True
def movemint(self):
keys = pygame.key.get_pressed()
if keys [pygame.K_LEFT] :
self.x -= 4
if keys [pygame.K_RIGHT] :
self.x += 4
if self.x < 0:
self.x += 4
if self.x > 665:
self.x -= 4
def left(self):
return self.x
def right(self):
return self.x+34
def up(self):
return self.y
def down(self):
return self.y+75
Second class that spawn trees that are coming from above
class Baum():
def __init__(self ,pos_x , pos_y ,pscreen ,pschlitten):
self.green = (0,100,0)
self.braun = (139,69,19)
self.red = (255,0,0)
self.x = pos_x
self.y = pos_y
self.screen = pscreen
self.Schlitten = pschlitten
def draw(self):
pygame.draw.polygon(self.screen ,self.green , [(self.x+50 ,self.y-95),(self.x+0 , self.y-10),
(self.x+100,self.y-10)])
pygame.draw.rect(self.screen , self.braun , [self.x+43,self.y-10,15,30])
pygame.draw.polygon(self.screen , self.green , [(self.x+50 , self.y-95), (self.x+5 , self.y-
25), (self.x+95,self.y-25)])
pygame.draw.polygon(self.screen , self.green , [(self.x+50 , self.y-95), (self.x+10 , self.y-
40), (self.x+90,self.y-40)])
pygame.draw.polygon(self.screen , self.green , [(self.x+50 , self.y-95), (self.x+15, self.y-
53), (self.x+85,self.y-53)])
def bewegung(self):
self.y += 5
def spawn(self):
if self.y > 600:
self.y = -50
self.x = random.randrange(0,700)
This is the collision but its unfinished
def collision(self):
if self.y > 385:
self.Schlitten.hit()
#Objekt der Klasse Schlitten erzeugen
spieler1 = Schlitten(350,400,screen)
Score = score(Baum)
#Objekt der Klasse Baum erzeugen
Baum1 = Baum(500,0 ,screen , spieler1)
Baum2 = Baum(300,-525 , screen , spieler1)
Baum3 = Baum(100,-1050 , screen, spieler1)
schrift = pygame.font.SysFont("comicsans" , 30 , True )
# -------- Haupt-Schleife -----------
while not done:
# Ändert den Wert von done auf True, falls Spiel-Fenster geschlossen wird
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- hier Zeichenbefehle ergänzen---
# Screen mit weiß fuellen
screen.fill((255,255,255))
pygame.mixer.music.set_volume(0.1)
Score.anzeigen()
# Schlitten zeichnen
spieler1.draw()
spieler1.movemint()
# Baeume zeichnen
Baum1.draw()
Baum1.bewegung()
Baum1.spawn()
Baum1.collision()
Baum2.draw()
Baum2.bewegung()
Baum2.spawn()
Baum2.collision()
Baum3.draw()
Baum3.bewegung()
Baum3.spawn()
Baum3.collision()
Ok, firstly is the standard response to these sort of questions: If you use the PyGame Sprite functions, after some extra work initially, your program will be easier to write and maintain.
To make good collisions on arbitrary objects, first you need a "bounding box". This is a rectangle which surrounds your object.
Looking at the code for the Schlitten/Sleigh I have to calculate this from the various drawing co-ordinates (but I'm only doing a quick/rough job). It looks like from Schlitten.x and Schlitten.y the rendering extends another 31 pixels in x and 75 pixels in y. You may want to temporarily add some code to draw the bounding-box to check it.
So to define a collision function, we need a PyGame Rect.
class Schlitten:
def __init__( self ):
self.x = px
self.y = py
self.width = 31
self.height = 75
self.rect = pygame.Rect( px, py, self.width, self.height )
As you can see from the code, a PyGame Rect just needs the co-ordinates. The .rect will need to be updated as the object moves, but we can do that right before the collision test. We added the self.width and self.height so our code is not peppered with meaningless numbers all over the place. Also if the drawing of the sleigh changes, these numbers need be adjusted only in one place.
Anyway, so now for the collision function:
class Schlitten:
...
def collideWith( self, other_rect ):
""" Has the sleigh collided with the other object """
self.rect.x = self.x # update the rect position
self.rect.y = self.y
collision = self.rect.colliderect( other_rect )
return collision
Make a similar change for your Baum class - at least to the point where the code has a baum.rect.
class Baum():
def __init__( self, pos_x, pos_y, pscreen, pschlitten ):
self.x = pos_x
self.y = pos_y
self.width = 50
self.height = 95
self.rect = pygame.Rect( pos_x, pos_y, self.width, self.height )
def bewegung( self ):
self.y += 5
self.rect.y += 5
def spawn( self ):
if self.y > 600:
self.y = -50
self.x = random.randrange(0,700)
self.rect.x = x
self.rect.y = y
This then allows the code to quickly and easily check for collisions:
alles_baumen = [ Baum1, Baum2, Baum3 ] # all tree objects
# main loop
while not exiting:
...
for baum in alles_baumen: # for each tree
baum.draw()
baum.bewegung()
baum.spawn()
if ( speiler1.collideWith( baum.rect ) ): # player hits tree?
speiler1.kollision() # Oh-noes!
Note: The tree-drawing function paints the trees as-if the y co-ordinate is the bottom-left corner. The changes I made do not account for this, so either change the drawing code, or change the position of the Baum.rect to suit this negative-y layout.

Why are my balls sticking together? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm making a clone of Ballz, a mobile game where you have to shoot a whole bunch of balls at blocks that break after multiple hits. It's like BrickBreaker on steroids. I've got it working mostly, but I can't figure out how to shoot the balls one after another. I know from testing that at the time of shooting, the balls are at different places, but immediately after that they occupy the same space.
Oh btw, the way that I'm keeping them separate is by making the balls go further back outside of the screen. So you can imagine it like setting them all up one behind the other, off screen, below the bottom of the player.
Here's my code:
import pygame
import math
import random
from vector import *
backgroundColor = (0, 0, 0)
ballColor = (255, 255, 255)
sizeOfOneBlock = 50.0
realDimension = 600.0
blockNumberInLine = int(realDimension/sizeOfOneBlock)
size = [int(realDimension), int(realDimension)]
# eg. probability(1/3)
def probability(chance):
return random.random() <= chance
def abs(x):
if x>=0:
return x
else:
return -x
# the classes used:
# Block, BlockHandler, Ball, Player
class Block():
def __init__(self, strength, i, j):
self.strength = strength
# i and j are numbers between 0 and blockNumberInLine-1
self.i, self.j = i, j
self.refreshStats()
def refreshStats(self):
self.color = (100, 224, 89)
def display(self, Surface):
pygame.draw.rect(Surface, (0, 0, 255), (self.i*sizeOfOneBlock, self.j*sizeOfOneBlock, sizeOfOneBlock, sizeOfOneBlock), 0)
class BlockHandler():
def __init__(self):
self.blockList = []
self.blockPositions = []
def resetPositionArray(self):
self.blockPositions = []
for block in self.blockList:
self.blockPositions.append([block.i*sizeOfOneBlock, block.j*sizeOfOneBlock])
def addNewLayer(self, gameLevel):
# move every existing block down
for block in self.blockList:
block.j += 1
# add new layer
for i in range(blockNumberInLine):
if probability(1/3):
# gameLevel determines the strength of the block
self.blockList.append(Block(gameLevel, i, 0))
# after all blocks are loaded, do this
self.resetPositionArray()
def displayBlocks(self, Surface):
for block in self.blockList:
block.display(Surface)
class Ball():
def __init__(self, posVector, moveVector):
self.posVector = posVector
self.moveVector = moveVector
self.radius = 2
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
def move(self):
self.posVector.add(self.moveVector)
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
def display(self, Surface):
pygame.draw.circle(Surface, ballColor, (self.x, self.y), self.radius)
def changeDirection(self, tuple):
# east
if tuple[0]>0:
self.moveVector.x = abs(self.moveVector.x)
# west
if tuple[0]<0:
self.moveVector.x = -abs(self.moveVector.x)
# south
if tuple[1]>0:
self.moveVector.y = abs(self.moveVector.y)
# north
if tuple[1]<0:
self.moveVector.y = -abs(self.moveVector.y)
def collisionDetect(self, blockX, blockY, blockSize, circleX, circleY, circleRadius):
xDeflect, yDeflect = 0, 0
# if in the same column
if (circleX>=blockX) and (circleX<=(blockX+blockSize)):
# if touching block from above or below
distance = circleY-(blockY+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
# either 1 or -1
if distance!=0:
yDeflect = distance/abs(distance)
# if in the same row
if (circleY>=blockY) and (circleY<=(blockY+blockSize)):
# if touching block from left or right
distance = circleX-(blockX+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
if distance!=0:
xDeflect = distance/abs(distance)
return [xDeflect, yDeflect]
def checkForCollisions(self, blockPositions):
# walls
if (self.x<=(0+self.radius)):
# east
self.changeDirection([1,0])
if (self.x>=(realDimension-self.radius)):
# west
self.changeDirection([-1,0])
if (self.y<=(0+self.radius)):
# south
self.changeDirection([0,1])
# blocks
for pos in blockPositions:
collision = self.collisionDetect(pos[0], pos[1], sizeOfOneBlock, self.x, self.y, self.radius)
self.changeDirection(collision)
class Player():
def __init__(self, posVector):
self.posVector = posVector
self.x = int(self.posVector.x)
self.y = int(self.posVector.y)
self.level = 1
self.numberOfBalls = 3
self.balls = []
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
self.balls.append(Ball(self.posVector, moveVector=Vector(0.0, 0.0)))
# print(ball)
def placeBalls(self, separateVector):
# self.resetBalls()
for j in range(len(self.balls)):
ball = self.balls[j]
for i in range(j):
ball.posVector.subtract(separateVector)
def display(self, Surface):
# possibly change color
pygame.draw.circle(Surface, ballColor, (self.x, self.y), 20)
def displayBalls(self, Surface):
for ball in self.balls:
ball.display(Surface)
def updateBalls(self, blockHandler):
for ball in self.balls:
ball.move()
ball.checkForCollisions(blockPositions=blockHandler.blockPositions)
def main():
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Ballz")
done = False
clock = pygame.time.Clock()
blockHandler = BlockHandler()
blockHandler.addNewLayer(1)
playerPosition = Vector(realDimension/2, realDimension-10)
player = Player(posVector=playerPosition)
player.resetBalls()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
# JFF
if event.key == pygame.K_w:
blockHandler.addNewLayer(1)
# for debugging
if event.key == pygame.K_d:
for ball in player.balls:
print(ball.posVector.x, ball.posVector.y)
print(ball.moveVector.x, ball.moveVector.y)
print("")
if event.key == pygame.K_r:
player.resetBalls()
if event.type == pygame.MOUSEBUTTONUP:
mousePos = pygame.mouse.get_pos()
player.shootVector = Vector(mousePos[0]-player.x, mousePos[1]-player.y).shortenTo(1)
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector.subtract(player.shootVector)
ball.moveVector = player.shootVector
# test
print(ball.posVector.x, ball.posVector.y)
print(ball.moveVector.x, ball.moveVector.y)
print("")
# LOGIC
player.updateBalls(blockHandler)
# DRAW
screen.fill(backgroundColor)
blockHandler.displayBlocks(screen)
player.displayBalls(screen)
player.display(screen)
pygame.display.flip()
# 60 frames per second
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()
Edit: Forgot to add the vector class.
class Vector():
def __init__(self, x=0, y=0):
self.x, self.y = x, y
def magnitude(self):
return ((self.x)**2 + (self.y)**2)**0.5
def shortenTo(self, radius):
magnitude = self.magnitude()
unitX = self.x/magnitude
unitY = self.y/magnitude
return Vector(unitX*radius, unitY*radius)
def add(self, addedVector):
self.x += addedVector.x
self.y += addedVector.y
def subtract(self, subtractedVector):
self.x -= subtractedVector.x
self.y -= subtractedVector.y
def printCoordinates(self):
print(self.x, self.y)
Sorry, no reproduction, your balls are fine:
No, but the problem you have is with mutable objects.
When you set
ball.moveVector = player.shootVector
you set all moveVector's to the same object, so every collision detection will change the direction of all balls simultaneosly. Simplest fix:
ball.moveVector = player.shootVector + Vector(x=0, y=0)
EDIT
I used a different vector module, in your case you can either use copy.copy or create a custom __add__ method:
def __add__(self, other):
if not isinstance(other, Vector)
raise ValueError
return Vector(self.x+other.x, self.y+other.y)
(This comes inside the Vector class, likewise for subtraction and mult.)
END EDIT
Also there are some problems with the way you reset when the balls leave the image and you should prevent the player from clicking again until the balls are reset, but I guess that comes in later development.
Appendix
Note: I'm working in Python 3 and either I installed a different vector module, or they changed a lot, so had to change some syntax there as well. Hope it helps :)
import pygame
import math
import random
from vector import *
backgroundColor = (0, 0, 0)
ballColor = (255, 255, 255)
sizeOfOneBlock = 50.0
realDimension = 600.0
blockNumberInLine = int(realDimension/sizeOfOneBlock)
size = [int(realDimension), int(realDimension)]
# eg. probability(1/3)
def probability(chance):
return random.random() <= chance
def abs(x):
if x>=0:
return x
else:
return -x
# the classes used:
# Block, BlockHandler, Ball, Player
class Block():
def __init__(self, strength, i, j):
self.strength = strength
# i and j are numbers between 0 and blockNumberInLine-1
self.i, self.j = i, j
self.refreshStats()
def refreshStats(self):
self.color = (100, 224, 89)
def display(self, Surface):
pygame.draw.rect(Surface, (0, 0, 255), (self.i*sizeOfOneBlock, self.j*sizeOfOneBlock, sizeOfOneBlock, sizeOfOneBlock), 0)
class BlockHandler():
def __init__(self):
self.blockList = []
self.blockPositions = []
def resetPositionArray(self):
self.blockPositions = []
for block in self.blockList:
self.blockPositions.append([block.i*sizeOfOneBlock, block.j*sizeOfOneBlock])
def addNewLayer(self, gameLevel):
# move every existing block down
for block in self.blockList:
block.j += 1
# add new layer
for i in range(blockNumberInLine):
if probability(1/3):
# gameLevel determines the strength of the block
self.blockList.append(Block(gameLevel, i, 0))
# after all blocks are loaded, do this
self.resetPositionArray()
def displayBlocks(self, Surface):
for block in self.blockList:
block.display(Surface)
class Ball():
def __init__(self, posVector, moveVector):
self.posVector = posVector
self.moveVector = moveVector
self.radius = 2
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
def move(self):
self.posVector += self.moveVector
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
def display(self, Surface):
pygame.draw.circle(Surface, ballColor, (self.x, self.y), self.radius)
def changeDirection(self, tuple):
# east
if tuple[0]>0:
self.moveVector['x'] = abs(self.moveVector['x'])
# west
if tuple[0]<0:
self.moveVector['x'] = -abs(self.moveVector['x'])
# south
if tuple[1]>0:
self.moveVector['y'] = abs(self.moveVector['y'])
# north
if tuple[1]<0:
self.moveVector['y'] = -abs(self.moveVector['y'])
def collisionDetect(self, blockX, blockY, blockSize, circleX, circleY, circleRadius):
xDeflect, yDeflect = 0, 0
# if in the same column
if (circleX>=blockX) and (circleX<=(blockX+blockSize)):
# if touching block from above or below
distance = circleY-(blockY+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
# either 1 or -1
if distance!=0:
yDeflect = distance/abs(distance)
# if in the same row
if (circleY>=blockY) and (circleY<=(blockY+blockSize)):
# if touching block from left or right
distance = circleX-(blockX+0.5*blockSize)
if abs(distance)<=(0.5*blockSize+circleRadius):
if distance!=0:
xDeflect = distance/abs(distance)
return [xDeflect, yDeflect]
def checkForCollisions(self, blockPositions):
# walls
if (self.x<=(0+self.radius)):
# east
self.changeDirection([1,0])
if (self.x>=(realDimension-self.radius)):
# west
self.changeDirection([-1,0])
if (self.y<=(0+self.radius)):
# south
self.changeDirection([0,1])
# blocks
for pos in blockPositions:
collision = self.collisionDetect(pos[0], pos[1], sizeOfOneBlock, self.x, self.y, self.radius)
self.changeDirection(collision)
class Player():
def __init__(self, posVector):
self.posVector = posVector
self.x = int(self.posVector['x'])
self.y = int(self.posVector['y'])
self.level = 1
self.numberOfBalls = 3
self.balls = []
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
x = Vector(x=j, y=j) - Vector(x=j, y=j)
self.balls.append(Ball(self.posVector, x))
# print(ball)
def placeBalls(self, separateVector):
# self.resetBalls()
for j in range(len(self.balls)):
ball = self.balls[j]
for i in range(j):
ball.posVector -= separateVector
def display(self, Surface):
# possibly change color
pygame.draw.circle(Surface, ballColor, (self.x, self.y), 20)
def displayBalls(self, Surface):
for ball in self.balls:
ball.display(Surface)
def updateBalls(self, blockHandler):
for ball in self.balls:
ball.move()
ball.checkForCollisions(blockPositions=blockHandler.blockPositions)
def main():
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Ballz")
done = False
clock = pygame.time.Clock()
blockHandler = BlockHandler()
blockHandler.addNewLayer(1)
playerPosition = Vector(x=realDimension/2, y=realDimension-10)
player = Player(posVector=playerPosition)
player.resetBalls()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
# JFF
if event.rrrr == pygame.K_w:
blockHandler.addNewLayer(1)
# for debugging
if event.key == pygame.K_d:
for ball in player.balls:
print(ball.posVector['x'], ball.posVector['y'])
print(ball.moveVector['x'], ball.moveVector['y'])
print("")
if event.key == pygame.K_r:
player.resetBalls()
if event.type == pygame.MOUSEBUTTONUP:
mousePos = pygame.mouse.get_pos()
player.shootVector = Vector(x=mousePos[0]-player.x, y=mousePos[1]-player.y) / ((mousePos[0]-player.x)**2 + (mousePos[1]-player.y))**.5
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector -= player.shootVector
ball.moveVector = player.shootVector + Vector(x=0, y=0)
# test
print(ball.posVector['x'], ball.posVector['y'])
print(ball.moveVector['x'], ball.moveVector['y'])
print("")
# LOGIC
player.updateBalls(blockHandler)
# DRAW
screen.fill(backgroundColor)
blockHandler.displayBlocks(screen)
player.displayBalls(screen)
player.display(screen)
pygame.display.flip()
# 60 frames per second
clock.tick(60)
main()
Since you're passing the player's self.posVector to the ball instances and then just assign it to their posVector attributes, the positions of the player and the balls all refer to the same Vector object in the memory. You can instead make copies of the vector, for example with the copy module, so that every ball.posVector refers to a separate vector object.
First import the copy function (it creates shallow copies).
from copy import copy
Then copy the vector objects before you pass them.
def resetBalls(self):
self.balls = []
for j in range(self.numberOfBalls):
self.balls.append(
Ball(copy(self.posVector), moveVector=Vector(0.0, 0.0)))
# ...
for ball in player.balls:
for i in range(player.balls.index(ball)*10):
ball.posVector.subtract(player.shootVector)
ball.moveVector = copy(player.shootVector)
I also recommend using pygame's Vector2 class instead of your own Vector, because it is more feature-rich and efficient.
And abs is a built-in function.

atan2 isn't providing me with the angle I want

I'm trying to write a game in pygame, involving a moving object with a "turret" that swivels to follow a mouse. As of now, I'm mostly trying to expand upon examples, so the code's not entirely mine (credit to Sean J. McKiernan for his sample programs); however, this portion is. Below is my code; I use the center of rect (the "base" shape and the point around which the "turret" swivels) as the base point, and the position of the mouse as the other point. By subtracting the mouse's displacement from the displacement of the "center," I effectively get a vector between the two points and find the angle between that vector and the x-axis with atan2. Below is the code I use to do that:
def get_angle(self, mouse):
x_off = (mouse[0]-self.rect.centerx)
y_off = (mouse[1]-self.rect.centery)
self.angle = math.degrees(math.atan2(-y_off, x_off) % 2*math.pi)
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)
According to multiple tutorials I've reviewed, this SHOULD be the correct code; however, I discovered later that those tutorials (and, in fact, this tutorial) were all for Python 2.7, while I am trying to write in Python 3.6. I don't think that should make a difference in this scenario, though. As it stands, the view appears to depend entirely upon the "character's" position on the screen. If the "character" is in one corner, the reaction of the "turret" is different than the reaction if the "character" is in the middle of the screen. However, this shouldn't matter; the position of the "character" relative to the mouse is the exact same no matter where on the screen they are. Any ideas, or do I need to supply more code?
Edit: Apparently, more code is required. Rather than attempt to extricate only the entirely necessary parts, I've provided the entire code sample, so everyone can run it. As a side note, the "Bolt" things (intended to fire simple yellow blocks) don't work either, but I'm just trying to get the arm working before I start in on debugging that.
Edit the second: I have discovered that the "Bolt" system works within a certain distance of the origin (0,0 in the window coordinate system), and that the arm also works within a much lesser distance. I added the line Block(pg.Color("chocolate"), (0,0,100,100)) under the "walls" grouping as a decision point, and the block was positioned in the top left corner. I've corrected Bolt by changing screen_rect to viewport in the control loop; however, I don't know why the "arm" swinging is dependent on adjacency to the origin. The positions of the mouse and "character" SHOULD be absolute. Am I missing something?
"""
Basic moving platforms using only rectangle collision.
-Written by Sean J. McKiernan 'Mekire'
Edited for a test of "arms"
"""
import os
import sys
import math
import pygame as pg
CAPTION = "Moving Platforms"
SCREEN_SIZE = (700,700)
BACKGROUND_COLOR = (50, 50, 50)
COLOR_KEY = (255, 255, 255)
class _Physics(object):
"""A simplified physics class. Psuedo-gravity is often good enough."""
def __init__(self):
"""You can experiment with different gravity here."""
self.x_vel = self.y_vel = 0
self.grav = 0.4
self.fall = False
def physics_update(self):
"""If the player is falling, add gravity to the current y velocity."""
if self.fall:
self.y_vel += self.grav
else:
self.y_vel = 0
class Player(_Physics, object):
def __init__(self,location,speed):
_Physics.__init__(self)
HAND = pg.image.load("playertst2.png").convert()
HAND.set_colorkey(COLOR_KEY)
self.image = pg.image.load('playertst.png').convert()
self.rect = self.image.get_rect(topleft=location)
self.speed = speed
self.jump_power = -9.0
self.jump_cut_magnitude = -3.0
self.on_moving = False
self.collide_below = False
self.original_hand = HAND
self.fake_hand = self.original_hand.copy()
self.hand = self.original_hand.copy()
self.hand_rect = self.hand.get_rect(topleft=location)
self.angle = self.get_angle(pg.mouse.get_pos())
def check_keys(self, keys):
"""Find the player's self.x_vel based on currently held keys."""
self.x_vel = 0
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.x_vel -= self.speed
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.x_vel += self.speed
def get_position(self, obstacles):
"""Calculate the player's position this frame, including collisions."""
if not self.fall:
self.check_falling(obstacles)
else:
self.fall = self.check_collisions((0,self.y_vel), 1, obstacles)
if self.x_vel:
self.check_collisions((self.x_vel,0), 0, obstacles)
def check_falling(self, obstacles):
"""If player is not contacting the ground, enter fall state."""
if not self.collide_below:
self.fall = True
self.on_moving = False
def check_moving(self,obstacles):
"""
Check if the player is standing on a moving platform.
If the player is in contact with multiple platforms, the prevously
detected platform will take presidence.
"""
if not self.fall:
now_moving = self.on_moving
any_moving, any_non_moving = [], []
for collide in self.collide_below:
if collide.type == "moving":
self.on_moving = collide
any_moving.append(collide)
else:
any_non_moving.append(collide)
if not any_moving:
self.on_moving = False
elif any_non_moving or now_moving in any_moving:
self.on_moving = now_moving
def check_collisions(self, offset, index, obstacles):
"""
This function checks if a collision would occur after moving offset
pixels. If a collision is detected, the position is decremented by one
pixel and retested. This continues until we find exactly how far we can
safely move, or we decide we can't move.
"""
unaltered = True
self.rect[index] += offset[index]
self.hand_rect[index] += offset[index]
while pg.sprite.spritecollideany(self, obstacles):
self.rect[index] += (1 if offset[index]<0 else -1)
self.hand_rect[index] += (1 if offset[index]<0 else -1)
unaltered = False
return unaltered
def check_above(self, obstacles):
"""When jumping, don't enter fall state if there is no room to jump."""
self.rect.move_ip(0, -1)
collide = pg.sprite.spritecollideany(self, obstacles)
self.rect.move_ip(0, 1)
return collide
def check_below(self, obstacles):
"""Check to see if the player is contacting the ground."""
self.rect.move_ip((0,1))
collide = pg.sprite.spritecollide(self, obstacles, False)
self.rect.move_ip((0,-1))
return collide
def jump(self, obstacles):
"""Called when the user presses the jump button."""
if not self.fall and not self.check_above(obstacles):
self.y_vel = self.jump_power
self.fall = True
self.on_moving = False
def jump_cut(self):
"""Called if player releases the jump key before maximum height."""
if self.fall:
if self.y_vel < self.jump_cut_magnitude:
self.y_vel = self.jump_cut_magnitude
def get_angle(self, mouse):
x_off = (mouse[0]-self.rect.centerx)
y_off = (mouse[1]-self.rect.centery)
self.angle = math.degrees(math.atan2(-y_off, x_off) % (2*math.pi))
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.hand_rect.center)
"""
offset = (mouse[1]-self.hand_rect.centery, mouse[0]-self.hand_rect.centerx)
self.angle = math.atan2(-offset[0], offset[1]) % (2 * math.pi)
self.angle = math.degrees(self.angle)
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(center=self.rect.center)
self.angle = 135-math.degrees(math.atan2(*offset))
self.hand = pg.transform.rotate(self.original_hand, self.angle)
self.hand_rect = self.hand.get_rect(topleft=self.rect.topleft)
"""
def pre_update(self, obstacles):
"""Ran before platforms are updated."""
self.collide_below = self.check_below(obstacles)
self.check_moving(obstacles)
def update(self, obstacles, keys):
"""Everything we need to stay updated; ran after platforms update."""
self.check_keys(keys)
self.get_position(obstacles)
self.physics_update()
def get_event(self, event, bolts):
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
bolts.add(Bolt(self.rect.center))
elif event.type == pg.MOUSEMOTION:
self.get_angle(event.pos)
def draw(self, surface):
"""Blit the player to the target surface."""
surface.blit(self.image, self.rect)
surface.blit(self.hand, self.hand_rect)
class Bolt(pg.sprite.Sprite):
def __init__(self, location):
pg.sprite.Sprite.__init__(self)
"""self.original_bolt = pg.image.load('bolt.png')"""
"""self.angle = -math.radians(angle-135)"""
"""self.image = pg.transform.rotate(self.original_bolt, angle)"""
"""self.image = self.original_bolt"""
self.image=pg.Surface((5,10)).convert()
self.image.fill(pg.Color("yellow"))
self.rect = self.image.get_rect(center=location)
self.move = [self.rect.x, self.rect.y]
self.speed_magnitude = 5
"""self.speed = (self.speed_magnitude*math.cos(self.angle), self.speed_magnitude*math.sin(self.angle))"""
"""self.speed = (5,0)"""
self.done = False
def update(self, screen_rect, obstacles):
self.move[0] += self.speed_magnitude
"""self.move[1] += self.speed[1]"""
self.rect.topleft = self.move
self.remove(screen_rect, obstacles)
def remove(self, screen_rect, obstacles):
if not self.rect.colliderect(screen_rect):
self.kill()
class Block(pg.sprite.Sprite):
"""A class representing solid obstacles."""
def __init__(self, color, rect):
"""The color is an (r,g,b) tuple; rect is a rect-style argument."""
pg.sprite.Sprite.__init__(self)
self.rect = pg.Rect(rect)
self.image = pg.Surface(self.rect.size).convert()
self.image.fill(color)
self.type = "normal"
class MovingBlock(Block):
"""A class to represent horizontally and vertically moving blocks."""
def __init__(self, color, rect, end, axis, delay=500, speed=2, start=None):
"""
The moving block will travel in the direction of axis (0 or 1)
between rect.topleft and end. The delay argument is the amount of time
(in miliseconds) to pause when reaching an endpoint; speed is the
platforms speed in pixels/frame; if specified start is the place
within the blocks path to start (defaulting to rect.topleft).
"""
Block.__init__(self, color, rect)
self.start = self.rect[axis]
if start:
self.rect[axis] = start
self.axis = axis
self.end = end
self.timer = 0.0
self.delay = delay
self.speed = speed
self.waiting = False
self.type = "moving"
def update(self, player, obstacles):
"""Update position. This should be done before moving any actors."""
obstacles = obstacles.copy()
obstacles.remove(self)
now = pg.time.get_ticks()
if not self.waiting:
speed = self.speed
start_passed = self.start >= self.rect[self.axis]+speed
end_passed = self.end <= self.rect[self.axis]+speed
if start_passed or end_passed:
if start_passed:
speed = self.start-self.rect[self.axis]
else:
speed = self.end-self.rect[self.axis]
self.change_direction(now)
self.rect[self.axis] += speed
self.move_player(now, player, obstacles, speed)
elif now-self.timer > self.delay:
self.waiting = False
def move_player(self, now, player, obstacles, speed):
"""
Moves the player both when on top of, or bumped by the platform.
Collision checks are in place to prevent the block pushing the player
through a wall.
"""
if player.on_moving is self or pg.sprite.collide_rect(self,player):
axis = self.axis
offset = (speed, speed)
player.check_collisions(offset, axis, obstacles)
if pg.sprite.collide_rect(self, player):
if self.speed > 0:
self.rect[axis] = player.rect[axis]-self.rect.size[axis]
else:
self.rect[axis] = player.rect[axis]+player.rect.size[axis]
self.change_direction(now)
def change_direction(self, now):
"""Called when the platform reaches an endpoint or has no more room."""
self.waiting = True
self.timer = now
self.speed *= -1
"""class Spell(pg.sprite.Sprite):
def __init__(self, location, angle)"""
class Control(object):
"""Class for managing event loop and game states."""
def __init__(self):
"""Initalize the display and prepare game objects."""
self.screen = pg.display.get_surface()
self.screen_rect = self.screen.get_rect()
self.clock = pg.time.Clock()
self.fps = 60.0
self.keys = pg.key.get_pressed()
self.done = False
self.player = Player((50,875), 4)
self.viewport = self.screen.get_rect()
self.level = pg.Surface((1000,1000)).convert()
self.level_rect = self.level.get_rect()
self.win_text,self.win_rect = self.make_text()
self.obstacles = self.make_obstacles()
self.bolts = pg.sprite.Group()
def make_text(self):
"""Renders a text object. Text is only rendered once."""
font = pg.font.Font(None, 100)
message = "You win. Celebrate."
text = font.render(message, True, (100,100,175))
rect = text.get_rect(centerx=self.level_rect.centerx, y=100)
return text, rect
def make_obstacles(self):
"""Adds some arbitrarily placed obstacles to a sprite.Group."""
walls = [Block(pg.Color("chocolate"), (0,980,1000,20)),
Block(pg.Color("chocolate"), (0,0,20,1000)),
Block(pg.Color("chocolate"), (980,0,20,1000))]
static = [Block(pg.Color("darkgreen"), (250,780,200,100)),
Block(pg.Color("darkgreen"), (600,880,200,100)),
Block(pg.Color("darkgreen"), (20,360,880,40)),
Block(pg.Color("darkgreen"), (950,400,30,20)),
Block(pg.Color("darkgreen"), (20,630,50,20)),
Block(pg.Color("darkgreen"), (80,530,50,20)),
Block(pg.Color("darkgreen"), (130,470,200,215)),
Block(pg.Color("darkgreen"), (20,760,30,20)),
Block(pg.Color("darkgreen"), (400,740,30,40))]
moving = [MovingBlock(pg.Color("olivedrab"), (20,740,75,20), 325, 0),
MovingBlock(pg.Color("olivedrab"), (600,500,100,20), 880, 0),
MovingBlock(pg.Color("olivedrab"),
(420,430,100,20), 550, 1, speed=3, delay=200),
MovingBlock(pg.Color("olivedrab"),
(450,700,50,20), 930, 1, start=930),
MovingBlock(pg.Color("olivedrab"),
(500,700,50,20), 730, 0, start=730),
MovingBlock(pg.Color("olivedrab"),
(780,700,50,20), 895, 0, speed=-1)]
return pg.sprite.Group(walls, static, moving)
def update_viewport(self):
"""
The viewport will stay centered on the player unless the player
approaches the edge of the map.
"""
self.viewport.center = self.player.rect.center
self.viewport.clamp_ip(self.level_rect)
def event_loop(self):
"""We can always quit, and the player can sometimes jump."""
for event in pg.event.get():
if event.type == pg.QUIT or self.keys[pg.K_ESCAPE]:
self.done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump(self.obstacles)
elif event.type == pg.KEYUP:
if event.key == pg.K_SPACE:
self.player.jump_cut()
elif event.type == pg.MOUSEMOTION or event.type == pg.MOUSEBUTTONDOWN:
self.player.get_event(event, self.bolts)
def update(self):
"""Update the player, obstacles, and current viewport."""
self.keys = pg.key.get_pressed()
self.player.pre_update(self.obstacles)
self.obstacles.update(self.player, self.obstacles)
self.player.update(self.obstacles, self.keys)
self.update_viewport()
self.bolts.update(self.screen_rect, self.obstacles)
def draw(self):
"""
Draw all necessary objects to the level surface, and then draw
the viewport section of the level to the display surface.
"""
self.level.fill(pg.Color("lightblue"))
self.obstacles.draw(self.level)
self.level.blit(self.win_text, self.win_rect)
self.player.draw(self.level)
self.bolts.draw(self.level)
self.screen.blit(self.level, (0,0), self.viewport)
def display_fps(self):
"""Show the programs FPS in the window handle."""
caption = "{} - FPS: {:.2f}".format(CAPTION, self.clock.get_fps())
pg.display.set_caption(caption)
def main_loop(self):
"""As simple as it gets."""
while not self.done:
self.event_loop()
self.update()
self.draw()
pg.display.update()
self.clock.tick(self.fps)
self.display_fps()
if __name__ == "__main__":
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
pg.display.set_caption(CAPTION)
pg.display.set_mode(SCREEN_SIZE)
PLAYERIMG = pg.image.load("playertst.png").convert()
PLAYERIMG.set_colorkey(COLOR_KEY)
run_it = Control()
run_it.main_loop()
pg.quit()
sys.exit()
The % 2*pi unnecessary, and your get_angle function has no return value, but you do an assignment to self.angle = self.get_angle, but that is not the issue. The issue is that the mouse position is relative to the screen (i.e. clicking in the top right area of your game screen will always yield (0,480) if your screen is 640x480), while the position of the (character) rectangle is given in your game play area, which is larger than the screen, ergo if you move the character and thus the view shifts, you are getting coordinates in two different coordinate systems. You will have to keep track of where the view is in your game play area and add the offset to the mouse coordinates.

Pygame health / maxhealth = 0.0 and not 0.66 [duplicate]

This question already has answers here:
How can I force division to be floating point? Division keeps rounding down to 0?
(11 answers)
Closed 5 years ago.
I am trying to make a healthbar in pygame and it is supposed to show in percent. The formula that I'm using is (lives/mlives)*100, mlives stands for max lives.
When the game begins, player has 3 lives to start with. Each time the shielding gets down to 0 or less, -1 is removed from lives. So on the first run everything works perfectly fine. But when -1 if removed from lives the health bar shows 0 and i have tried to print the formula on terminal without multiplying it with 100 to see what it would show when lives / mlives.
now when player has 3 lives the formula gives 1.0 and that is 100%
when the player has 2 lives the formula gives 0.0 instead of 0.66 which is 66%
player has 3 lives
player has 2 lives
game.py -- funciton name is draw_health_bar
#!/usr/bin/python
import os, sys, player, enemy, config, random
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
FPS, BGIMG = 30, 'FlappyTrollbg.jpg'
# initialize game
pygame.init()
screen = pygame.display.set_mode((config.WIDTH,config.HEIGHT))
pygame.display.set_caption("FlappyTroll - Python2.7")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255,255,255))
img_dir = path.join(path.dirname(__file__), 'img')
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.width = config.WIDTH
self.height = config.HEIGHT
self.image = pygame.image.load(path.join(img_dir,image_file)).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
#self.rect.left, self.rect.top = location
self.rect.x, self.rect.y = location
self.speedx = 5
def update(self):
self.rect.x -= self.speedx
if self.rect.x <= -config.WIDTH:
self.rect.x = config.WIDTH
def draw_shield_bar(surf, x, y, percent):
if percent <= 0:
percent = 0
bar_length = 100
bar_height = 10
fill = (percent / 100) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.green, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_health_bar(surf, x, y, percent):
bar_length = 100
bar_height = 10
fill = float(percent) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.red, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_text(surf, text, size, x, y):
font_ = pygame.font.SysFont("Arial", size)
show_kills = font_.render(text, True, config.white)
surf.blit(show_kills, (x, y))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def newSprite(group, obj):
group.add(obj)
def main():
all_sprites = pygame.sprite.Group()
bgs = pygame.sprite.Group()
creature = pygame.sprite.Group()
attack = pygame.sprite.Group()
eattack = pygame.sprite.Group()
bgs.add(Background(BGIMG, [0,0]))
bgs.add(Background(BGIMG, [config.WIDTH,0]))
troll = player.FlappyTroll()
creature.add(troll)
for i in range(0,4):
newSprite(all_sprites, enemy.TrollEnemy())
# variable for main loop
running = True
# init umbrella
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for e in all_sprites:
if (pygame.time.get_ticks() - e.starttime) >= e.delay:
newEAtk = enemy.EnemyAttack(e.rect.x, e.rect.y)
eattack.add(newEAtk)
e.starttime = pygame.time.get_ticks()
keys = pygame.key.get_pressed()
if (keys[pygame.K_SPACE]) and (pygame.time.get_ticks() - troll.starttime) >= troll.delay:
newAtk = player.FlappyAttack(troll.rect.x, troll.rect.y)
attack.add(newAtk)
troll.starttime = pygame.time.get_ticks()
b_gets_hit = pygame.sprite.groupcollide(eattack, attack, True, True)
p_gets_hit_eatk = pygame.sprite.groupcollide(eattack, creature, True, False)
gets_hit = pygame.sprite.groupcollide(all_sprites, attack, True, True)
p_gets_hit = pygame.sprite.groupcollide(all_sprites, creature, True, False)
if gets_hit or p_gets_hit:
newEnemy = enemy.TrollEnemy()
newSprite(all_sprites, newEnemy)
for p in creature:
if p_gets_hit or p_gets_hit_eatk:
troll.shield -= random.randint(1,5)*1.5
if troll.shield <= 0:
troll.lives -= 1
troll.shield = 100
if troll.lives == 0:
print "#--- GAME OVER ---#"
break
screen.fill([255, 255, 255])
# update
bgs.update()
all_sprites.update()
creature.update()
attack.update()
eattack.update()
# draw
bgs.draw(screen)
all_sprites.draw(screen)
creature.draw(screen)
attack.draw(screen)
eattack.draw(screen)
draw_shield_bar(screen, 5, 5, troll.shield)
draw_health_bar(screen, 5, 20, (troll.lives/troll.mlives))
draw_text(screen, ("Lives: "+str(troll.lives)), 20, (config.WIDTH / 2), 0)
print float(troll.lives/troll.mlives)
# flip the table
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
player.py (object name is Troll in game.py)
import pygame, config
from pygame.locals import *
from os import path
from random import randint
img_dir = path.join(path.dirname(__file__), 'img')
class FlappyTroll(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = 64
self.height = 64
self.image = pygame.image.load(path.join(img_dir,"flappytroll.png")).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
self.rect.x = self.width*2
self.rect.y = config.HEIGHT/2-self.height
self.speedy = 5
self.starttime = pygame.time.get_ticks()
self.delay = 500
self.shield = 100
self.lives = 3
self.mlives = 3
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
self.rect.y -= self.speedy*2
elif self.rect.y < config.HEIGHT-self.height*1.5:
self.rect.y += self.speedy
Try dividing by 3.0 rather than just 3. I'm pretty sure this will work as it is what Python 2's integer division is coded as. Hope this helps :-) P.S. If you do any more additions, subtractions, divisions or multiplications, be sure to put .0 at the end unless you want another D.P. e.g. 2 * 7.0.

Categories