Shorter way to make colliderect in if statement work? - python

I have two objects:
#Object1
enemigo=pygame.image.load("enemigo.png").convert_alpha()
enemigo=pygame.transform.scale(enemigo, (100, 100))
enemigo_rectangulo=enemigo.get_rect(center=(1000, 100))
#Object2
enemigo2=pygame.image.load("enemigo2.png").convert_alpha()
enemigo2=pygame.transform.scale(enemigo2, (140, 140))
enemigo_rectangulo2=enemigo2.get_rect(center=(1400, 50))
And I want to make an if statement if they collide with a third object. This code actually works but is too long:
if personaje_rectangulo.colliderect(enemigo_rectangulo) or personaje_rectangulo.colliderect(enemigo_rectangulo2):
And there's an error when I try to write:
if personaje_rectangulo.colliderect(enemigo_rectangulo, enemigo_rectangulo2):
So, what is wrong? Is there a shorter way to write it?

The first thing you can do is use shorter variable names, such as personaje and enemigo.
Additionally, break the statement into smaller pieces by using variables:
hit_enemy1 = personaje_rectangulo.colliderect(enemigo_rectangulo)
hit_enemy2 = personaje_rectangulo.colliderect(enemigo_rectangulo2)
if hit_enemy1 or hit_enemy2:
pass
Alternatively, if you store all of the enemies in a list, you can use any():
if any(personaje_rectangulo.colliderect(enemigo_rectangulo) for enemigo_rectangulo in todos_enemigos):
pass
Now this is also getting long so creating a function to help can shorten the line of code:
def collision(person, enemy):
return person.colliderect(enemy)
if any(collision(person, enemy) in todos_enemigos):
pass
Another solution is to create a Enemy class which encapsulates the data and behavior of an enemy. This is a bit more advanced, but definitely a good exercise to learn what classes are and how to use them.

Use collidelist():
Test whether the rectangle collides with any in a sequence of rectangles. The index of the first collision found is returned. If no collisions are found an index of -1 is returned.
if personaje_rectangulo.collidelist([enemigo_rectangulo, enemigo_rectangulo2]) >= 0:

Related

Is there a way to mirror a python function?

I am a student taking my first python class and we are using Turtle to draw an image. In an effort to save time I am trying to create a mirror function that can flip one functions results over the y-axis by a given number of pixels. For the sake of simplicity I created some functions to speed up my coding. Here is how my code works:
units, north, east, south, west = desiredPixelsPerUnit, 90, 0, 270, 180
def main():
eye(30, 15)
eye(40, 15)
def left():
myTurtle.left(90)
def draw(count):
distance = units * count
myturtle.forward(distance)
def singleStep(xDirection, yDirection, stepCount):
down()
for step in range(stepCount):
if yDirection == north:
point(north)
draw(1)
if xDirection == east:
point(east)
draw(1)
etc..
def eye(xPosition, yPosition):
....
draw(3)
left()
draw(2)
left()
....
singleStep(east, north, 1)
singleStep(west, north, 2)
etc....
All of this gives me the following
Result of running eye() in main twice:
What I am trying to create is a function that is passed another function then will look at what is being executed. if it is left() or right() run the opposite. If it is point (x, y) add 180 to x. If it has a function call that inside the function then it checks that as well for lefts or rights. Something like this.
def mirror(function):
for action in function:
if action == left():
execute right()
...
elif action == singleStep():
newFunction = singleStep()
for action in newFunction:
if:
statement above
else:
statement below
else:
execute initial action
I am still very new to coding and programming. I have tried using arrays, associative arrays and lists, using eval and more. The time spent trying to figure it out has been far longer than writing a separate instruction list for the left and the right hahah but I really want to figure out how to do something like this.
You could step back from calling left() and right() directly. Just create your own functions your_left() and your_right() and use them everytime. Then use a global variable called mirror. This variable will work as a flag for you. So you just set mirror to True when you want to mirror the output.
Your functions your_right() and your_left() will be looking something like this:
def your_right():
left() if mirror else right()
def your_left():
right() if mirror else left()
And then you are free to mirror all your outputs, if you want to. I hope i could help!
Here is how you can do that using yield:
import turtle
def function(): # Your function here, with the action calls replaced with yielding the un-called action, along with the argument
for _ in range(4):
yield turtle.forward, 100
yield turtle.right, 90
def mirror(function):
actions = {turtle.right: turtle.left,
turtle.left: turtle.right,
turtle.forward: turtle.backward,
turtle.backward: turtle.forward}
for action, argument in function():
actions[action](argument)
mirror(function)
Breaking it down:
When defining the function that will be passed into the mirror function,
do not call the turtle commands. Instead, use yield to yield the functions and arguments
as tuples, so that the mirror function will be able to access which functions should be called.
In the mirror function, define a dictionary of turtle commands, actions, that would be used to mirror with.
Iterate through the yielded turtle actions from the function passed into the brackets of the mirror function, and unpack the tuples into action, argument pairs to be used to mirror.

Python3: Deleting enemy from dynamic array when colliding with the player

I am creating a sidescroller shooter game in which the enemies(drones)spawn contantly on the right side of the screen. The recently created enemies are appended to a list drones Each enemy goes towards the left of the screen. Once they are out of the screen, they get removed from the list.
I also want to remove the drones when they collide with the player. The following code works properly as long as there are multiple objects in the drones list, but when the list has only one item (so one drone is on the screen) that drone does not get deleted upon collision.
I have no idea why the first list item cannot be destroyed.
drones = []
class Drone
#other call methods
def hit(self):
del drones[drones.index(self)]
def generate_enemy():
global drones
if len(drones) < 20:
if (random.randint(1,100) == 1):
drones.append(Drone(screenWidth, random.randint(300,500))
def main():
global drones
while True:
#main loop stuff happening
if condition == True:
generate_enemy():
#main loop stuff happening
if player and (len(drones) > 0):
for i in range(len(drones)-1):
if drones[i].hitbox.colliderect(player.hitbox):
drones[i].hit()
main()
The "correct" way to do this in pygame is to let your classes inherit from Sprite, then use a Group instead of a simple list to store your enemies, and using pygame.sprite.spritecollide for collision detection with the dokill argument set to True.

Python, Pygame - Rect not working in list

I have a list of rects, just by saying a = [] then doing a.append(pygame.Rect(1,1,32,32)). I can draw this easily by just doing
for blocks in a:
pygame.draw.rect(screen, WHITE, blocks)
But when I do
for blocks in a:
if blocks.colliderpoint(pygame.mouse.get_pos()):
#code
An error occurs, saying it must be an Rect like style. I don't understand
if blocks.colliderpoint(pygame.mouse.get_pos()):
maybe because of the typo?
Change .colliderpoint into .collidepoint and try again. That should do.

pygame - particle effects

I'm making a 2-d game using Pygame.
I want to add particle effects into the game I'm working on. I want to do things like spawn smoke, fire, blood, etc. I'm curious is there an easy way to do this? I don't really know even where to start.
I just need a base case I could expand upon..
Pls Help.
You might want to just make a class made of rects that go up and randomly go to the right or left each time it is updated for the smoke. Then make a ton of them whenever you want it. I'll try to make an example code below, but I can't guarntee it will work. You can make similar classes for the other particle effects.
class classsmoke(pygame.Rect):
'classsmoke(location)'
def __init__(self, location):
self.width=1
self.height=1
self.center=location
def update(self):
self.centery-=3#You might want to increase or decrease this
self.centerx+=random.randint(-2, 2)#You might want to raise or lower this as well
#use this to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put this somewhere within your game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, i)
Another option would be to make the class just a tuple, like this:
class classsmoke():
'classsmoke(location)'
def __init__(self, location):
self.center=location
def update(self):
self.center[1]-=3
self.center[0]+=random.randint(-2, 2)
#to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put inside game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i.center[0], i.center[1], 1, 1))
Or, to avoid classes completly:
#to create smoke:
smoke=[]
for i in range(20):
smoke.append(insert location here)
#put within your game loop
for i in smoke:
i[1]-=3
i[0]+=random.randint(-2, 2)
if i[1]<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i[0], i[1], 1, 1))
Pick your preference, and do something similar for other particle effects.
Check the Library for particle effects PyIgnition

How would I create random locations for many groups of sprites?

I have tried a for loop with the blit and draw methods and using different variables for " PlayerSprite " and " Treegroup "
for PlayerSprite in Treegroup:
surface.blit(PlayerSprite,(random.randrange(100,500),random.randrange(100,600)))
also tried
SPRITES=[]
for Sprites in range(10):
Sprites= PlayerSprite
SPRITES.append(Sprites)
all I get are errors
screen=pygame.display.set_mode((640,480))
background1=pygame.image.load("C:\Pygame-Docs\examples\data\Random Map.bmp")
class Tree1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load('C:\Pygame-Docs\examples\data\Tree 1.bmp')
self.image=self.image.convert()
self.rect=self.image.get_rect()
self.rect.centerx=random.randrange(10,100)
self.rect.centery=random.randrange(10,100)
# Makes a group of trees
Howmanytrees=random.randrange(5,10)
Trees=[]
for tree in range(Howmanytrees):
trees=Tree1()
Trees.append(trees)
# Howmany groups
for Treegroup in range(10):
Treegroup=Trees
# Places groups
PlayerSprite=pygame.sprite.Group(Treegroup)
# keeps loop ( game ) going until canceled
keepgoing=True
while keepgoing:
for event in pygame.event.get():
if event.type==pygame.QUIT:
keepgoing=False
# actually draws screen
screen.blit(background1,(0,0))
PlayerSprite.draw(screen)
pygame.display.flip()
This code only displays 5 to 10 trees " Trees=[] "
and nothing else. I have worked on this problem for over a week , read many tutorials, looked on many websites, nothing seems to work. I must be overlooking or missing somethig. I thought this would be easy!
Thanks so much!
As far as I understand what you want to achieve, the below code should help you. I kept it very very simple regarding python syntax, as you seems to be a newbie (for experienced programmers: yes, what I wrote below is horrible python code, but I believe the OP can understand it and it may help).
The key is that if you want to get several groups of trees, you must have a loop within a loop. The inner loop put the trees inside the group, and the outer loop put several groups. Of course you can (and should) certainly hide the inner loop behind some function or class.
# Howmany groups ? say 3
trees_groups = []
number_of_groups = 3
for _ in range(number_of_groups):
# Choose a base position for my group of trees
base_x = random.randrange(0,530)
base_y = random.randrange(0,370)
# Makes a group of trees
trees=[]
number_of_trees = random.randrange(5,10)
for _ in range(number_of_trees):
one_tree = Tree1()
trees.append(one_tree)
for tree in trees:
tree.rect.centerx += base_x
tree.rect.centery += base_y
trees_groups.append(tree)
# Places groups
PlayerSprite=pygame.sprite.Group(trees_groups)
Some after notes:
And as other posters said, you should not use capitalized variables as you do. The python usage is to keep them for classes
Also looping using range when the base variant is not used is bad practice. I emphasized this by using underline as a variable name for the loop variant when it is not used.
I would use randint and move_ip to get what you want. Here is a code snippet from my own game that works just as well:
self.rect.move_ip(random.randint(minX, maxX), random.randint(minY, maxY))
the four variables minX, maxX, minY, maxY form a rectangle where the sprite can be placed. In your case, the trees would be placed along the entire screen, but with a reduced max X and Y range so trees won't clip through the bottom of the screen.
Also, use a Group class to store your trees rather than a list. A list stops the spawning of multiple trees, while a Group does. Here is how to call it:
Treegroup = pygame.sprite.Group
and to add a sprite to the group:
Treegroup.add(Tree1(screen))
Make sure the class itself has screen in its init, like so:
def __init__(self, screen)
Once that's done, your code should look something like this:
for Treegroup in range(10):
Treegroup.add(Tree(screen))
[...]
class Tree(pygame.sprite.Sprite):
def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image('tree.png', -1)
self.rect.move_ip(random.randint(0, 800), random.randint(0, 600))
self.area = screen.get_rect()
It doesn't really make much sense to me.
for tree in range(Howmanytrees):
trees=Tree1()
Trees.append(trees)
Your loop here is doing nothing at all. tree should be a number between 0 and Howmanytrees. Again, the following block isn't indented so it's not part of the loop. Even so, the block still wouldn't work. Also, you're confusing yourself and us with variable names. Trees is the object? trees is the list? Don't do this. Seriously.
No idea what the following code is up to.
# Howmany groups
for Treegroup in range(10):
Treegroup=Trees
Create your SpriteGroup passing in the aforementioned trees list? Or am I missing something :) TreeGroup = Trees 10 times is just going to do that 10 times. You are not using the loop variant. The only thing that modifies during the loop. Even so, this entire block of code is useless.
while keepgoing:
for event in pygame.event.get():
if event.type==pygame.QUIT:
keepgoing=False
This is going to cause a nice infinite loop. It is evaluating the keepgoing variable constantly. This will never get set to false unless the user quits, but also it will never display anything on the screen. Lose this. Use this instead:
for event in pygame.event.get():
if event.type == QUIT:
return
This will not cause an infinite loop as there are only so many events to be processed per tick. Then the program will loop around and do the process or updating, rendering and getting input again.

Categories