Game ai Line of Sight Python - python

How would I check to see whether the player is within range of a mob? (I am making a top down game in pyglet) I am making it so that the mobs follow and attack the player if he is within a certain distance of them but I am unsure how to efficiently do this. Would I need to do an "if" statement to see if x > mob.x - 50 and x < mob.x + 50 etc?
I have a class for the mobs
class zombie(pyglet.sprite.Sprite):
def __init__(self, image, x, y, batch,trees):
pyglet.sprite.Sprite.__init__(self, image, x, y, batch=None)
I then used several functions as the different actions they could be doing
def move(self):
...
def idle(self):
...
The player's position is "player.x" and "player.y" (the same for the mobs but with "zombie instead of player)
As Joran said, I think finding the distance betweens the mob and the player's coordinates is the best approach and I will make another function to check the distance.
Sorry if this was unclear

you would probably need to calculate the distance between the monster and the player
sqrt((mob.x-player.x)**2 + (mob.y-player.y)**2)
you could probably simplify it and get rid of the sqrt ...

Related

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

How can a turtle be moved up then down in a for loop?

So I'm learning python in a virtual programming class and I have an assignment where I have to create a picture using Turtle Graphics. I'm using things like for loops and programmer-defined functions in the program but I'm having a problem with something I want to do in my picture. I'm drawing clouds in the sky and I'm trying to get the clouds to be drawn at different positions. I'm increasing the x coordinate by increments of 40 but I'm trying to get the cloud to be placed higher and lower as it goes across. It's at the end of this list of code:
import turtle
def backFill(b, c, x, y, l, h):
b.penup()
b.setpos(x, y)
b.color(c)
b.pendown()
b.begin_fill()
for side in range(2):
b.forward(l)
b.left(90)
b.forward(h)
b.left(90)
b.end_fill()
def drawCloud(c, x, y):
c.penup()
c.setpos(x, y)
c.pendown()
c.color("grey")
c.begin_fill()
for side in range(5):
c.circle(10)
c.left(80)
c.end_fill()
def main():
print("Cars")
joe = turtle.Turtle()
joe.speed(0)
backFill(joe,"green",-200,-100,400,25)
backFill(joe,"black",-200,-75,400,75)
backFill(joe,"green",-200,0,400,25)
backFill(joe,"sky blue",-200,25,400,110)
x=-192.5
for side in range(10):
backFill(joe,"yellow",x,-40,25,5)
x=x+40
x=-180
y=100
for side in range(15):
drawCloud(joe,x,y)
x=x+40
y=y-10
main()
Currently the clouds slowly descend as each one is drawn but what I'm trying to do is make clouds at different heights like one cloud is at 100 the next 90 then back to 100, etc. I tried things like y=y-10, y+10 to see if the first time it repeated it would go down 10 then the next time it would go up 10.
https://gyazo.com/3ad5268231b3217b81636cc070573b75
tl;dr/simpler explanation: I'm trying to move a looped picture up and down so it's not simply in a straight line. How would I move it down one time then up another time within a for loop?
You could check whether side is even or odd to decide whether to increment or decrement your cloud location. Like so:
for side in range(15):
drawCloud(joe,x,y)
x=x+40
if(side%2==0):
y=y-10
else:
y=y+10
for side in range(5):
drawCloud(joe,x,y)
x=x+40
y=y-10
drawCloud(joe,x,y)
x=x+40
y=y+10

Why doesn't this circle disappear upon collision?

These seem to be the relevant portions of a slightly larger program. These are essentially the collision detection functions. bubbles is the original list of circles. I want the circle collided with to disappear on impact. I'm fairly sure my problem lies down there in "if collide ==" conditional.
def getDistance(point1,point2):
a= point1.getX()
b= point2.getX()
c= point1.getY()
d= point2.getY()
distance= math.sqrt((b-a)**2 + ((d-c)**2))
return distance
def balloonBubbleCollide(balloon,bubble):
point1 = balloon.getCenter()
point2= bubble.getCenter()
distance= getDistance(point1, point2)
if distance <= 30:
return True
def check(balloon, bubbles, window):
for bubble in bubbles:
collide = balloonBubbleCollide(balloon, bubble)
if collide == True:
bubbles.remove(bubble)
Assume main is running them in proper order. Just don't want to bog the post down with code.
You should not modify a list using remove while iterating over it.
To filter out the colliding bubbles use instead something like:
def check(balloon, bubbles, window):
bubbles[:] = [bubble for bubble in bubbles
if not baloonBubbleCollide(balloon, bubble)]
the code shown will create a first new list of bubble objects to keep (using a list comprehension) and then replace the current content of the bubbles list with it at once.

If player comes within a radius of enemy,change state

So I've been teaching myself some pygame for the past few months, doing a spaceship game.
I have multiple enemy spaceships that guard a certain area. If the player comes within a certain radius of them, I want to change the enemy's state. What's the best way to do this for a beginner, without using vectors?
You can create a method in your program called is_close and then call it for each player and enemy comparison. It would look something like this:
def is_close(object1, object2, distance):
return math.hypot(object2.x-object1.x, object2.y-object1.y) < float(distance)
... #rest of your code
while True: #This is your main while loop
... #rest of your code
for enemy in enemies:
if is_close(player, enemy, 25):
enemy.state = new_state #change state, this may simply be a color change

Implementing an A* algorithm with pygame I find my unit "back-tracks"

So I have been fooling around with Pygame and have run into a problem that I can solve, but only in-elegantly.
Basically I have a unit(at this point just a red square) on a plain field of black. If I click the Unit and then click anywhere else on the field the unit proceeds to that point. The algorithm itself works pretty flawlessly, even once obstacles are introduced it handles path-finding quickly.
My problem is in my implementation. The unit is a 16 by 16 square, the field is thus divided into 16 by 16 squares. If I change the unit's destination while it is in transit it sometimes "back-tracks" to the previous "way-point" before continuing on its new route.
I understand why this happens, The path finding module treats each square as a point on a grid, so that the pixel coordinate of point (2,2) is actually (32,32), when I pass the mouse coordinates into the path method I divide them by 16 getting rounded down integers(so (34,37) becomes (2,2)) so while the unit may be between actual "grid-points" the path finding module finds the path from its last actual way point, not it's current location. when it passes the new route to the unit, the unit has to "back-track" in order to follow the route the path-finding module has found
clearly every game developer that does any kind of path-finding has solved this so I'm hoping that I can find a more elegant solution.
I don't want to find a path along every single pixel on the screen just waypoints, but the backtracking is a little irritating. Any suggestions?
as suggested below is the concerned code:
in the main .py file
if pygame.mouse.get_pressed()[0]: # if left mouse button pressed
mouse_x,mouse_y = pygame.mouse.get_pos()
if selected: # checks if a unit is selected
unit.getRoute((mouse_x/16,mouse_y/16))
in the unit.py file
def getRoute(self,target):
self.route = path((self.x/16,self.y/16), target)
def getNode(self):
if self.route:
self.node = route.pop(0)
dif_x,dif_y = self.node[0] - self.x, self.node[1] - self.y
if dif_x > 0:
self.vector_x = self.speed
if dif_x < 0:
self.vector_x = self.speed * -1
else:
self.vector_x = 0
if dif_y > 0:
self.vector_y = self.speed
if dif_x < 0:
self.vector_y = self.speed * -1
else:
self.vector_y = 0
else:
self.node = None
self.vector_x = 0
self.vector_y = 0
def update(self):
if self.route or self.node:
if self.route and not self.node:
self.getNode()
if (self.x,self.y) == self.node:
self.getNode()
self.x += self.vector_x
self.y += self.vector_y
self.rect.topleft = (self.x,self.y)
You should take care of managing this first step in a special way. This because even if the unit is already moving the cell specified with it remains the same until it reaches the next one.
Now I suggest you to make sure to update the cell of the unit as soon as half of the movement has been done (so that the pathfinder will search for the path from the next cell and not from the previous one). In addition you should manage the special situation with something like
if unit is standing still
compute pathfinding as you would normally do
else
compute pathfinding from the unit cell
discard the first cell of the pathfinding route (since it would backtrack)
compute the route directly to the next cell (by drawing a correct path)
Of course this will be easier or harder according to how much freedom of movement you have, if you allow only orthogonal movements that it will be straightforward, if you allow diagonal movement then you should make care to not cross over an obstacle (then it depends on how much tolerance you want to small glitches, like the unit overlapping with a corner of an obstacle).

Categories

Resources