Syntax error in the pygame collision detection function python3 [duplicate] - python

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
so I try to program a 2d game platform like space invaders, but the problem is in the collision, when I try to define the collision, which I use in her characteristics, the characteristics of the classes (enemy coordinates and the bullet coordinates), the code won't start. And this is the result : `
def isc (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
^
SyntaxError: invalid syntax
this is the code if you need it :
############# LIBRARIES ####################
import pygame, sys, random
import math
from pygame.constants import SYSTEM_CURSOR_WAITARROW
from pygame import mixer
pygame.init()
############ WINDOW OPTIONS ################
Swidth = 800
Sheight =600
screen = pygame.display.set_mode((Swidth, Sheight))
pygame.display.set_caption("Space Invader By Mal4D")
icon = pygame.image.load('places/favicon.png')
pygame.display.set_icon(icon)
############# COLORS #######################
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
YELLOW = (255 , 255 , 0)
########### VARIANTS ######################
isJumping= False
LevelJump = 0.25
rocket = pygame.image.load("rocket/rocket.png")
space = pygame.image.load("places/bg.png")
bullet = pygame.image.load("rocket/bullet.png")
Sound = pygame.mixer.music.load("Sounds/laser.mp3")
Bgsound= pygame.mixer.music.load("Sounds/bg.mp3")
enemy = pygame.image.load("Enemy/enemy.png")
ax=random.randint(0,700)
by=random.randint(20,120)
Bgsound1 = mixer.Sound("Sounds/bg.mp3")
BullletSound = mixer.Sound("Sounds/laser.mp3")
############## Class Players ##########################
class Player():
def __init__(self,x,y,width,height):
self.x=x
self.y=y
self.width=width
self.height=height
self.step=10
self.speed=10
self.isJumping= False
def drawplayer (self,screen):
screen.blit(rocket, (rockeet.x, rockeet.y))
def drawscreen (self,screen):
screen.blit(space, (0,0))
rockeet = Player(350,500,50,50)
########### Bullet #####################
class Bullet():
def __init__(self, x, y , y_change, x_change, state):
self.x=x
self.y= y
self.y_change=y_change
self.x_change= x_change
self.state= state
def fire(x,y):
Bulllet.state = "fire"
screen.blit(bullet,(x+16, y+10))
Bulllet=Bullet(350,500,20,0,"ready")
**def isCollision (ax1, by1, cx, dy):
distance = math.sqrt(math.pow(ax1 - cx, 2) + (math.pow(by1- dy, 2)))
if distance <27:
return True
else:
return False**
################## Enemy ########################
class Enemy():
def __init__(self, x ,y, stepx,stepy):
self.x = x
self.y=y
self.stepx=stepx
self.stepy=stepy
def drawenemy(screen):
screen.blit(enemy,(ax,by))
eneemy = Enemy(random.randint(0,700),random.randint(20,120),3,35)
################# Clussion#########################
score = 0
def isc (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
distance = math.sqrt(math.pow(eneemy.x - Bulllet.x, 2) + (math.pow(eneemy.y- Bulllet.y, 2)))
if distance <27:
return True
else:
return False
################### Game Loop #####################################
#Bgsound1.play(180)
while 1:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
keys=pygame.key.get_pressed()
rockeet.drawscreen(screen)
Enemy.drawenemy(screen)
if keys[pygame.K_SPACE]:
if Bulllet.state == "ready":
BullletSound.play()
Bulllet.x=rockeet.x
Bulllet.y=rockeet.y
Bullet.fire(Bulllet.x, Bulllet.y)
if keys[pygame.K_LEFT] and rockeet.x - rockeet.step >= 0:
rockeet.x =rockeet.x-rockeet.step
if keys[pygame.K_RIGHT] and rockeet.x + rockeet.width +rockeet.step <= Swidth :
rockeet.x =rockeet.x+rockeet.step
if Bulllet.y <= 0:
Bulllet.y= 500
Bulllet.state = "ready"
if Bulllet.state == "fire":
Bullet.fire(Bulllet.x, Bulllet.y)
Bulllet.y -= Bulllet.y_change
########### Enemy Mouvement #########################################
collision = isCollision( eneemy.x, eneemy.y, Bulllet.x, Bulllet.y)
ax+= eneemy.stepx
if ax<= 0:
eneemy.stepx=3
by+= eneemy.stepy
elif ax >= 750:
eneemy.stepx=-3
by+= eneemy.stepy
if collision:
Bulllet.y= 500
Bulllet.state="ready"
eneemy.x= random.randint(0,700)
eneemy.y=random.randint(20,)
################## Collision ######################
################## OUTRO #######################################
rockeet.drawplayer(screen)
pygame.display.update()
the result:
def isCollision (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
^
SyntaxError: invalid syntax

The formal parameters of a function cannot be structure elements like eneemy.x. Either Change the names of the parameters:
def isc (enemy_x, enemy_y, bullet_x, bullet_y):
distance = math.sqrt(math.pow(enemy_x - bullet_x, 2) + (math.pow(enemy_y- bullet_y, 2)))
if distance <27:
return True
else:
return False
Or pass the objects to the function:
def isc (enemy, bullet):
distance = math.sqrt(math.pow(enemy.x - bullet.x, 2) + (math.pow(enemy.y- bullet.y, 2)))
if distance <27:
return True
else:
return False
collision = isCollision( eneemy.x, eneemy.y, Bulllet.x, Bulllet.y)
collision = isCollision(eneemy, Bulllet)
Note, that you can use math.hypot instead of math.sqrt and math.pow:
distance = math.sqrt(math.pow(enemy_x - bullet_x, 2) + (math.pow(enemy_y- bullet_y, 2)))
distance = math.hypot(enemy_x - bullet_x, enemy_y - bullet_y)

Related

My enemies stop moving if i move my player [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 2 years ago.
https://imgur.com/a/5aoTDwA
Sorry for the lag, but as visible my enemies(green) spawn but after if i move my player(gray) they sometimes stop moving until i move my player again.
But even after moving my player the pre existing enemies dont move closer to the player and retain their distance to the player
this is the file which contains the main loop of the game
this is the file which contains the player class
this is the file which contains the enemy class
and this is the file which is the main run file
i feel the problem exists in my main loop class which is as follows:
import pygame, random
from Folder.scripts.TopDown.script_files.td_Player import Player
from Folder.scripts.TopDown.script_files.td_enemy import Enemy
from Folder.scripts.TopDown.script_files.td_colours import colours
pygame.init()
class Main:
def __init__(self):
self.FPS = 60
self.WIDTH = self.HEIGHT = 1280
self.MAP = pygame.Surface((self.WIDTH, self.HEIGHT))
self.enemies = []
def main(self,WINDOW ,clock):
WINDOW.fill(colours['white'])
player = Player()
camera_pos = (0, 0)
enemy_time = 0
run = True
while run:
clock.tick(self.FPS)
enemy_y = random.randint(0, 1220)
enemy_x = random.randint(0, 1220)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > enemy_time:
enemy_time = current_time + 1000
self.enemies.append(Enemy(enemy_y, enemy_x))
camera_pos = player.movement(camera_pos)
self.WindowUpdate(WINDOW, player, camera_pos)
pygame.quit()
def WindowUpdate(self,WINDOW, player, camera_pos):
player_x, player_y = player.playerMove()
WINDOW.fill((255, 255, 255))
self.MAP.fill(colours['gray'])
player.renderPlayer(self.MAP)
WINDOW.blit(self.MAP, camera_pos)
for e in self.enemies:
e.renderEnemy(WINDOW)
e.enemyMovement(player_x, player_y)
pygame.display.update()
Enemy class:
import pygame, os, random, math
pygame.init()
# current_time = pygame.time.get_ticks()
# if current_time > self.enemy_time:
# self.enemy_time = current_time + 1000
# self.enemies.append()
class Enemy:
def __init__(self, enemy_y, enemy_x):
self.ENEMY_HEIGHT = self.ENEMY_WIDTH = 64
self.ENEMY_ACCELERATION = 5
self.enemy_rect = pygame.Rect(enemy_x, enemy_y, self.ENEMY_HEIGHT, self.ENEMY_WIDTH)
self.ENEMY = {
'up': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_up.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'down': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_down.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'left': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_left.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'right': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_right.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH))
}
self.enemy_state = 'up'
def enemyMovement(self, player_x, player_y):
ene_x, ene_y = player_x - self.enemy_rect.x, player_y - self.enemy_rect.y
dist = math.hypot(ene_x, ene_y)
ene_x, ene_y = ene_x // dist, ene_y // dist
self.enemy_rect.x += ene_x * self.ENEMY_ACCELERATION
self.enemy_rect.y += ene_y * self.ENEMY_ACCELERATION
print(self.enemy_rect.x, self.enemy_rect.y)
def renderEnemy(self, WINDOW):
WINDOW.blit(self.ENEMY[self.enemy_state] , (self.enemy_rect.x, self.enemy_rect.y))
I hope the imgur link to the vid showcasing my problem gets embedded. Any input or help will be appreciated
The result of ene_x // dist and ene_y // dist is always 0. To calculate the movement, you must use the / (division) operator rather than the // (floor division) operator
ene_x, ene_y = ene_x // dist, ene_y // dist
ene_x, ene_y = ene_x / dist, ene_y / dist
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the movement gets lost when the position of the object incremented:
self.enemy_rect.x += ene_x * self.ENEMY_ACCELERATION
self.enemy_rect.y += ene_y * self.ENEMY_ACCELERATION
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:
x, y = # floating point coordinates
rect.topleft = round(x), round(y)
Add floating point coordinates to the Enemy class:
class Enemy:
def __init__(self, enemy_y, enemy_x):
# [...]
self.enemy_rect = pygame.Rect(enemy_x, enemy_y, self.ENEMY_HEIGHT, self.ENEMY_WIDTH)
self.x, self.y = self.enemy_rect.topleft
# [...]
def enemyMovement(self, player_x, player_y):
ene_x, ene_y = player_x - self.x, player_y - self.y
dist = math.hypot(ene_x, ene_y)
ene_x, ene_y = ene_x / dist, ene_y / dist
self.x += ene_x * self.ENEMY_ACCELERATION
self.y += ene_y * self.ENEMY_ACCELERATION
self.enemy_rect = round(self.x), round(self.y)

How to delete a single pygame drawing from the screen?

When the big circle touches the little circles I want the little circle that it touched to disappear from the screen. However, I can't figure out how exactly you delete an individual drawing in pygame. How do I fix this issue? does pygame have this feature built-in?
from pygame import *
import random as rd
import math as m
init()
screen = display.set_mode((800, 600))
p_1_x = 200
p_1_y = 200
p_1_change_x = 0
p_1_change_y = 0
def p_1(x, y):
player_1 = draw.circle(screen, (0, 0, 0), (x, y), 15)
def pick_up(x, y, xx, yy):
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
# I think the code to delete should go here
pass
dots = []
locations = []
for i in range(5):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
locations.append((x, y))
while True:
screen.fill((255, 255, 255))
for events in event.get():
if events.type == QUIT:
quit()
if events.type == KEYDOWN:
if events.key == K_RIGHT:
p_1_change_x = 1
if events.key == K_LEFT:
p_1_change_x = -1
if events.key == K_UP:
p_1_change_y += 1
if events.key == K_DOWN:
p_1_change_y -= 1
if events.type == KEYUP:
if events.key == K_RIGHT or K_LEFT or K_UP or K_DOWN:
p_1_change_x = 0
p_1_change_y = 0
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
pick_up(p_1_x, p_1_y, locate[0], locate[1])
p_1(p_1_x, p_1_y)
display.update()
Your code was so messy and hard to maintain, first I made 2 classes for Balls & Dots.
I detect collision by pygame.Rect.colliderect, first I make 2 rectangle then I check the collision like this:
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
If collision detects I remove it from dots array in the while loop:
for dot in dots:
if pick_up(ball, dot): # if dot in range ball
dots.remove(dot)
dot.draw()
Here is the whole source:
from pygame import *
import random as rd
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
NUMBER_OF_DOTS = 5
class Ball():
SIZE = 15
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Ball.SIZE)
def move(self, vx, vy):
self.x += vx
self.y += vy
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Dot.SIZE)
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
init()
screen = display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
dots = []
ball = Ball(200,200)
# generate dots
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
# the main game loop
while True:
screen.fill((255, 255, 255))
keys=key.get_pressed()
for events in event.get():
keys=key.get_pressed()
if events.type == QUIT:
quit()
if keys[K_RIGHT]:
ball.move(+1,0)
if keys[K_LEFT]:
ball.move(-1,0)
if keys[K_UP]:
ball.move(0,-1)
if keys[K_DOWN]:
ball.move(0,+1)
for dot in dots:
dot.draw()
if pick_up(ball, dot):
dots.remove(dot)
ball.draw()
display.update()
time.delay(1) # Speed down
Update1:
PyGame Rectangle Collision
http://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
Update2:
I make a repo in the github and did some changes,
Dots are colorful, new dot gets random color and the ball gets bigger whenever eats a dot.
https://github.com/peymanmajidi/Ball-And-Dots-Game__Pygame
The code should delete it from the locations list so that it's not re-drawn in the future. You clear the screen each frame, so clearing + not-redrawing is "deleting".
Say you modified pick_up() to simply return True or False:
def pick_up(x, y, xx, yy):
result = False
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
result = True # It was picked
return result
Then as you iterate through the locations list drawing & checking for being picked, save the index of the picked circles, then remove them from the locations in a second step. Using the 2-step form means you don't have to worry about accidentally skipping items if you delete from the list as you iterate over it.
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
picked_up = [] # empty list to hold "picked" items
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
if ( pick_up(p_1_x, p_1_y, locate[0], locate[1]) ):
picked_up.append( i ) # save the index of anything "picked"
# remove any picked-up circles from the list
for index in sorted( picked_up, reverse=True ): # start with the highest index first
print( "Removing circle from location[%d]" % ( index ) ) # DEBUG
del( locations[ index ] )

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.

Pygame. How to make a rect change direction on collision (boundary check)

Part of an assignment I'm working on is making a ball bounce around the screen, I can make it move, but my boundary test doesn't seem to be working: the ball simply moves in direction instead of changing direction. So to clarify, what I want to ball to do is change direction as it hits the screen edge.
import sys
import pygame
SCREEN_SIZE = 750, 550
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER // 2
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
LEFT = 11
RIGHT = 12
pygame.init()
clock = pygame.time.Clock()
pygame.display.init()
font = pygame.font.SysFont("impact", 20)
pygame.display.set_caption("Breakout")
screen = pygame.display.set_mode(SCREEN_SIZE)
class Ball:
def __init__(self):
''' '''
self.ball = pygame.Rect(300, 730 -
BALL_DIAMETER,
BALL_DIAMETER, BALL_DIAMETER)
# Draw ball
def draw_ball(self):
pygame.draw.circle(screen,
WHITE, (self.ball.left
+ BALL_RADIUS, self.ball.top +
BALL_RADIUS), BALL_RADIUS)
# Updates the coordinates by adding the speed components
def move_ball(self, x, y):
self.xspeed = x
self.yspeed = y
self.ball = self.ball.move(self.xspeed, self.yspeed)
# bounds check
if self.ball.left <= 0:
self.ball.left = 0
self.xspeed = -self.xspeed
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.xspeed = -self.xspeed
if self.ball.top < 0:
self.ball.top = 0
self.yspeed = -self.yspeed
elif self.ball.top >= MAX_BALL_Y:
self.ball.top = MAX_BALL_Y
self.yspeed = -self.yspeed
# shows a message on screen, for testing purposes
class Text:
def show_message(self, message):
self.font = pygame.font.SysFont("impact", 20)
font = self.font.render(message,False, WHITE)
screen.blit(font, (200, 400))
class Game:
def __init__(self):
''' '''
def run(self):
b = Ball()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
# fps lock, screen fill and method call for input
clock.tick(60)
screen.fill(BLACK)
b.draw_ball()
b.move_ball(5, -5)
# used to keep track of various elements
# Text().show_message("P: " + str(p))
pygame.display.flip()
# Creates instance of the game class, and runs it
if __name__ == "__main__":
Game().run()
Your only call to move_ball uses a constant vector.
Since you never change the call parameters, the ball moves only that way.
b.move_ball(5, -5)
Yes, you change the vector components within move_ball when you hit a wall. However, on the next call, you change them back to the original values and move the ball in the original direction.
You have to initialize the vector outside move_ball, and then let the routine access the existing vector when it's called.

Python: pygame deleting image on mouse click and creating it again

im trying to make a simple game where the there are soldiers coming towards you and when you click them to "kill" them they go at the back of the screen and start to come towards you, so on.....
however i'm having trouble with pygame mouse click event and it just doesnt work.
heres my code so far:
import pygame, math
from random import randrange
import sys, math, pygame
from operator import itemgetter
def getKey(customobj):
return customobj.getKey()
class Point3D:
def __init__(self, imfiles, nfrm, x = 0, y = 0, z = 0):
self.x, self.y, self.z = float(x), float(y), float(z)
self.frms = []
self.nfrm=nfrm
self.index=0
for k in range(0,nfrm):
im=pygame.image.load(imfiles+'_'+str(k+1)+'.png')
im.set_colorkey((0,0,0))
self.frms.append(im)
def
project(self, win_width, win_height, fov, viewer_distance):
""" Transforms this 3D point to 2D using a perspective projection. """
factor = fov / (viewer_distance + self.z)
x = self.x * factor + win_width / 2
y = -self.y * factor + win_height / 2
return Point3D(x, y, self.z)
def draw3D(self, wsurface, fov, viewer_distance, max_depth):
win_width=wsurface.get_width()
win_height=wsurface.get_height()
factor = fov / (viewer_distance + self.z)
x = self.x * factor + win_width / 2
y = -self.y * factor + win_height / 2
size = int((1 - float(self.z) / max_depth) * 64)
im=pygame.transform.smoothscale(self.frms[self.index],(size,size))
try:
wsurface.blit(im, (x, y))
except:
print((x,y))
self.index=self.index+1
if self.index >= self.nfrm:
self.index=0
def getKey(self):
return -self.z
class StartField:
def __init__(self, num_stars, max_depth):
pygame.init()
myWin = pygame.display.set_mode((640, 450), 0, 32)
pygame.display.set_caption('Drawing')
self.screen = myWin.subsurface([0,0,640,400]);
self.txtwin = myWin.subsurface([0,400,640,50]);
pygame.display.set_caption("Task C")
self.clock = pygame.time.Clock()
self.num_stars = num_stars
self.max_depth = max_depth
self.init_stars()
def init_stars(self):
""" Create the starfield """
self.stars = []
for i in range(self.num_stars):
# A star is represented as a list with this format: [X,Y,Z]
star = Point3D('im',8,randrange(-25,25), randrange(-25,25), randrange(1, self.max_depth))
self.stars.append(star)
def move_and_draw_stars(self):
""" Move and draw the stars """
origin_x = self.screen.get_width() / 2
origin_y = self.screen.get_height() / 2
stars=sorted(self.stars,key = getKey)
for star in stars:
# The Z component is decreased on each frame.
star.z -= 0.05
# If the star has past the screen (I mean Z<=0) then we
# reposition it far away from the screen (Z=max_depth)
# with random X and Y coordinates.
if star.z <= 0:
star.x = randrange(-25,25)
star.y = randrange(-25,25)
star.z = self.max_depth
# Convert the 3D coordinates to 2D using perspective projection.
star.draw3D(self.screen, 128, 0, self.max_depth)
def run(self):
""" Main Loop """
bgPicture = pygame.transform.smoothscale(pygame.image.load('Starfield.jpg'),(self.screen.get_width(),self.screen.get_height()))
font = pygame.font.Font(None, 36)
while 1:
# Lock the framerate at 50 FPS.
self.clock.tick(50)
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
self.screen.blit(bgPicture, [0,0])
self.move_and_draw_stars()
# Text window outputs
self.txtwin.fill([200,200,200])
text = font.render("Total Score: ", 1, (10, 10, 10))
self.txtwin.blit(text, [5, 5])
pygame.display.update()
if __name__ == "__main__":
StartField(256, 24).run()
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("MuseUprising.mp3")
sounda.play()
To test for the left mouse button:
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
If give the soldier a pygame.Rect you can use that to check for collision with the mouse pointer like this:
mouse_pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0] and self.rect.collidepoint(mouse_pos):

Categories