This question already has answers here:
return statement in for loops [duplicate]
(6 answers)
Closed 3 years ago.
Hi I'm new to game dev and I'm using pygame, now I'm in this problem I have multiple enemies that the player has to avoid and kill simultaneously, but my collision detection function checks collision for one enemy at a time.
Aside from the first enemy in the list, the function does not work for other enemies from the list.
Here's the code for enemy creation and collision detection
class Enemy(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.width, self.height = 30, 30
self.vel = 10
self.color = (0, 0, 255)
self.rect = (x, y, self.width, self.height)
def create_enemies(n):
enemies = []
for i in range(n):
xcor = randint(0, (W - 50))
ycor = randint(0, H - H/2)
enemies.append(Enemy(xcor, ycor))
return enemies
n = 2
enemies = create_enemies(n)
def collision_detection(obj1, obj2list):
for obj2 in obj2list:
if (obj2.x < obj1.x < (obj2.x + obj2.width)) and (obj2.y < obj1.y < (obj2.y + obj2.height)):
return False
if (obj2.x < obj1.x < (obj2.x + obj2.width)) and (obj2.y < (obj1.y + obj1.height) < (obj2.y + obj2.height)):
return False
if (obj2.x < (obj1.x + obj1.width) < (obj2.x + obj2.width)) and (obj2.y < (obj1.y + obj1.height) < (obj2.y + obj2.height)):
return False
if (obj2.x < (obj1.x + obj1.width) < (obj2.x + obj2.width)) and (obj2.y < obj1.y < (obj2.y + obj2.height)):
return False
if obj1.x == obj2.x and obj1.y <= obj2.y + obj2.height:
return False
if obj1.x == obj2.x and obj1.y + obj1.height >= obj2.y:
return False
else:
return True
Your problem is in your collision detection logic. Your logic is as follows:
main loop > call collision_detection > iterate over objects
It should be:
main loop > iterate over objects > call collision_detection
This is because if the first object in obj2list collides with obj1, it returns from the function and doesn't check the rest of the objects.
Related
I want to check if the mouse clicked on a line on Tkinter canvas or not, if on a line then which line.
I had made this to detect mouse clicks.
class Link:
def __init__(self,Node1,Node2,canvas,width=5):
if self not in canvas.LinkList:
self.start_coor = Node1.Centre
self.final_coor = Node2.Centre
self.Canvas = canvas
self.Width = width
self.Shape = canvas.create_line(self.start_coor,self.final_coor,width=width)
Node1.connected(Node2)
self.Canvas.LinkList.append(self)
self.Nodes = [Node1,Node2]
self.Clicked = False
dy = self.final_coor[1] - self.start_coor[1]
dx = self.final_coor[0] - self.start_coor[0]
self.m = dy/dx
self.c = self.start_coor[1] - self.m*self.start_coor[0]
def onLineCheck(self,x,y,field=False):
#y = mx + c
#y - mx - c = 0
if not field:
field = self.Width
if (x < self.start_coor[0] and x < self.final_coor[1]) or (x > self.start_coor[0] and x > self.final_coor[1]) or (y < self.start_coor[1] and y < self.final_coor[1]) or (y > self.start_coor[1] and y > self.final_coor[1]):
return False
temp = y - (self.m*x) - self.c
if abs(temp) <= field:
return True
return False
class InputCanvas(Canvas):
def __init__(self,master=None, **kw):
super().__init__(master,**kw)
self.NodeList = []
self.LinkList = []
self.Mode = "Nodes"
def anyLinkClicked(self,e):
x,y = getMousePosition(e)
for l in self.LinkList:
if l.onLineCheck(x,y):
return l
return False
Every time when a link is created, it will automatically append itself to the canvas.LinkList. I am sure this part of the code is working properly.
So far the program works well with 1 line on Canvas(even if I remove it and draw a new one it is still working) but cannot handle more than 1 line, it can only respond to the first line created. Even if I remove the first line, the second created line won't work.
I've tried printing out the result of each onLineCheck(), it seems like the loop is looping properly through each line but it is not catching mouse clicks.
Any idea to help?
The issue is on the following line:
if (x < self.start_coor[0] and x < self.final_coor[1]) or (x > self.start_coor[0] and x > self.final_coor[1]) or (y < self.start_coor[1] and y < self.final_coor[1]) or (y > self.start_coor[1] and y > self.final_coor[1]):
return False
x < self.final_coor[1] should be x < self.final_coor[0]
x > self.final_coor[1] should be x > self.final_coor[0]
Also you should also cater vertical line, i.e. dx is zero as your code will raise ZeroDivisionError: division by zero.
You can use Canvas.find_overlapping() function to find which Link is clicked:
def anyLinkClicked(self, e):
x, y = getMousePosition(e)
found = self.find_overlapping(x, y, x, y)
if found:
for l in self.LinkList:
if found[0] == l.Shape:
return l
return False
Why do I get a IndexError: list index out of range error message in my pygame game?
I'm making a simple sake game, but when I add to the snakes' length each time it eats an apple I get an error.
This is the collision function
def collision(x1, y1, x2, y2, bsize):
if x2 + bsize >= x1 >= x2:
if y2 + bsize >= y1 >= y2:
return True
return False
Here is the eat the apple and die if collide with itself function.
def eat(self): # if it eats the apple
for i in range(self.length):
if collision(apple.x, apple.y, snake.x[i], snake.y[i], apple.width): # entering the variables/coordinates
self.length += 1
apple.x_multiplier = random.randrange(row)
apple.y_multiplier = random.randrange(row)
apple.x = (r_distance * apple.x_multiplier) + 1 # spawning the apple
apple.y = (r_distance * apple.y_multiplier) + 1 # at a new coordinate
def collide(self): # if colide with itself
for i in range(2, self.length):
if collision(snake.x[0], snake.y[0], snake.x[i], snake.y[i], self.width): # enetring the variables/coordinates
self.alive = False # killing the snake
So I am trying to create a series of "boids" at random locations, which fly at random speeds, but I am having some trouble moving the rects which are in a list, although I can draw them. I am using a provided vector module, the entire code and the module can be found here. The png I am using for the sprites.
Update: I got a rect moving, by using the instance position vector instead of the class vector. But now only one boid is drawn. I suspect that more boids are drawn at the same exact position.
class Boid():
def __init__(self, screen):
self.bird = pygame.image.load("birdie.png")
self._pos = Vector2D(random.randint(0, screen.get_width()),
random.randint(0, screen.get_height()))
self._vel = Vector2D((random.randint(1, 10) / 5.0),
(random.randint(1, 10) / 5.0))
self.speed = random.randint(1, 5)
self.bird_rect = self.bird.get_rect(center=(self._pos.x, self._pos.y))
self._boids = []
def add_boid(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self._boids.append(Boid(screen))
def move_boids(self):
s = Screen()
#self.bird_rect.move_ip(self._vel.x, self._vel.y)
self._pos += (self._vel * self.speed)
#bounds check
if self._pos.x + self.bird_rect.width >= s.width:
self._pos.x = s.width - self.bird_rect.width
self._vel.x *= -1
elif self._pos.x <= 0:
self._pos.x = 0
self._vel.x *= -1
if self._pos.y - self.bird_rect.height <= 0:
self._pos.y = self.bird_rect.height
self._vel.y *= -1
elif self._pos.y >= s.height:
self._pos.y = s.height - self.bird_rect.height
self._vel.y *= -1
def draw_boids(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
print(len(self._boids))
for boid in self._boids:
self.boidRect = pygame.Rect(self.bird_rect)
#edit: changed boid._pos.x and y to self._pos.x and y
self.boidRect.x = self._pos.x
self.boidRect.y = self._pos.y
screen.blit(self.bird, self.boidRect)
You have to iterate over all boids in the self._boids list and update their _pos and bird_rect attributes to move them.
def move_boids(self):
s = Screen()
for boid in self._boids:
boid._pos += boid._vel * boid.speed
boid.bird_rect.center = boid._pos
# Bounds check.
if boid._pos.x + boid.bird_rect.width >= s.width:
boid._pos.x = s.width - boid.bird_rect.width
boid._vel.x *= -1
elif boid._pos.x <= 0:
boid._pos.x = 0
boid._vel.x *= -1
if boid._pos.y - boid.bird_rect.height <= 0:
boid._pos.y = boid.bird_rect.height
boid._vel.y *= -1
elif boid._pos.y >= s.height:
boid._pos.y = s.height - boid.bird_rect.height
boid._vel.y *= -1
You can also simplify the draw method a bit.
def draw_boids(self):
# Blit all boids at their rects.
for boid in self._boids:
screen.blit(boid.bird, boid.bird_rect)
Have an issue with a game of snake in Python im making where my collision check isnt working. I have written a function to check a collision between the snake and the food itself. When it collides it doesnt do anything, i have written it to undraw if it collides using the function, i also put in a print function to see if the function was working if i was using it and saw no print.
def collide(block1,block2):
if math.sqrt(((block2.getCenterX() - block1.getCenterX()) **2)+ ((block2.getCenterY() - block1.getCenterY())**2)) < BLOCK_SIZE:
print("true")
return True
else:
return False
------------------------------------------------------- not part of functiom
if collide(theSnake[0],food) == True:
food.undraw()
foodX = random.randint(BLOCK_SIZE, WIN_WIDTH-BLOCK_SIZE)
foodY = random.randint(BLOCK_SIZE, WIN_HEIGHT-BLOCK_SIZE)
food.draw()
theSnake.append(block)
else:
foodX = foodX
foodY = foodY
I'd suggest you modify your collide function to provide more information. eg.
def collide(block1,block2):
dx = block2.getCenterX() - block1.getCenterX()
dy = block2.getCenterY() - block1.getCenterY()
dist = math.sqrt(dx ** 2 + dy ** 2) # equivalent to math.hypot(dx, dy)
if dist < BLOCK_SIZE:
print("true")
return True
else:
print("false", dx, dy, dist)
return False
I Have Been Trying for days on how to do this. Basically You Control A Player, And zombies follow you.
Problem Is, I Cant Seem To Get The Zombies To Follow! I Tried If Statements For Example
if playerx > zombiex:
zombiex=zombiex - 2
screen.blit(zombie,(zombiex,zombiey))
aaaaaand That Didnt Work.... :/
Any Ideas?
Maybe this is what you searched for.
def length(x, y):
return (x ** 2 + y ** 2) ** .5
def norm(x, y):
_len = length(x, y)
return x / _len, y / _len
class ZombieController(object):
def __init__(self, zombie_view_range):
self._zombs = []
self.append = self._zombs.append
self._range = zombie_view_range
def NextFrame(self, player_pos):
px, py = player_pos
_range = self._range
for zombie in self._zombs:
x, y = zombie.position
dx, dy = px - x, py - y
_len = length(dx, dy)
if _len <= _range:
speed = zombie.speed
direction = norm(dx, dy)
zombie.positon = x + direction[0] * speed, y + direction[1] * speed
First, can't answer your whole questions as there's not enough information. How doesn't it work exactly?
Second, if you want the zombies to follow, you need thier coordinates to converge with the players so you need something like:
if playerx > zombiex:
zombiex = zombiex + max(zombiespeed, playerx - zombiex)
elif playerx < zombiex:
zombiex = zombiex - max(zombiespeed, zombiex - playerx)
NB:
I replace 2 with zombiespeed which you define elsewhere so you can change the speed in one place for future.
I use max() to ensure the zombie won't move PAST the player when very close.
You'd obviously do the same for the y direction too.