Problem with enemy movement towards player in pygame library - python

I ran into a problem with the pygame library while building my app.
A part of my code is as follows: at the beginning of the class in the init method
I create my enemies.
self.create_enemy
And then, that method is like this
def create_enemy(self):
self.available_space_x = 10
for enemy_number in range(self.sets.enemy_number):
self.enemy = Enemy(self)
self.available_space_x += 150
self.enemy.x = self.available_space_x
self.enemy.rect.x = self.enemy.x
self.enemys.add(self.enemy)
The work of this method is that it creates 5 enemies in different places and then adds it to the group of my enemies.
Next, part of the main method is as follows:
self.enemys.draw(self.screen)
self.checkenemymove()
self.update_enemys()
That is, it creates my enemies and then executes this function:
def checkenemymove(self):
if self.human.human_rect.x >= self.enemy.rect.x:
self.goenemleft = False
self.goenemright = True
elif self.human.human_rect.x < self.enemy.rect.x:
self.goenemright = False
self.goenemleft = True
This method makes the enemy move to the right if the player is ahead of the enemy (player side) or the enemy moves to the left if the player is behind the enemy.
And finally this method is executed:
def update_enemys(self):
if self.goenemright:
self.enemy.image = self.enemy.enemy_images[0]
self.enemy.x += self.sets.enemy_speed
self.enemy.rect.x = self.enemy.x
if self.goenemleft:
self.enemy.image = self.enemy.enemy_images[1]
self.enemy.x -= self.sets.enemy_speed
self.enemy.rect.x = self.enemy.x
The problem is that if I run the program, only the last enemy will do this, and wherever the player goes, the enemy will also move in that direction.
Please help me, thank you.
I wanted the enemy to move towards me, but only one enemy moves towards me.

Remove the self.enemy attribute from the class. You don't need it because all enemies are contained in the self.enemys pygame.sprite.Group. Therefore, you need only the group of enemies:
def create_enemy(self):
available_space_x = 10
for enemy_number in range(self.sets.enemy_number):
enemy = Enemy(self)
available_space_x += 150
enemy.x = available_space_x
enemy.rect.x = enemy.x
self.enemys.add(enemy)
Use a for-loop to move all enemies in the self.enemys list:
def update_enemys(self):
for enemy in self.enemys:
if self.goenemright:
enemy.image = self.enemy.enemy_images[0]
enemy.x += self.sets.enemy_speed
enemy.rect.x = enemy.x
if self.goenemleft:
enemy.image = self.enemy.enemy_images[1]
enemy.x -= self.sets.enemy_speed
enemy.rect.x = enemy.x

Thank you very much, I have come to the conclusion that I should do this:
def checkenemymove(self):
for enemy in self.enemys:
if self.human.human_rect.x >= enemy.rect.x:
enemy.image = self.enemy.enemy_images[0]
enemy.x += self.sets.enemy_speed
enemy.rect.x = enemy.x
elif self.human.human_rect.x < enemy.rect.x:
enemy.image = self.enemy.enemy_images[1]
enemy.x -= self.sets.enemy_speed
enemy.rect.x = enemy.x

Related

How do I make my rocket go back to its position when hitting the border?

I am new to programming and started with pygame zero. I am making a little game where you shoot a rocket to an alien. But my rocket keeps stuck to the border when fired, I made a reload function but I want it to go automatically ( when it hits the border or alien to go back to its normal position). Can anyone help me with that?
alien = Actor('alien', (100,100))
ship =Actor('ship', (500,400))
rocket_fire = Actor('rocket_fire', (500,400))
WIDTH = 1000
HEIGHT =500
def draw():
screen.clear()
screen.blit('space_back', (0,0))
rocket_fire.draw()
ship.draw()
alien.draw()
def move_ship(ship):
if keyboard.left:
ship.x -= 3
rocket_fire.x -= 3
elif keyboard.right:
ship.x += 3
rocket_fire.x += 3
elif keyboard.space:
animate(rocket_fire, pos = (ship.x,0))
elif keyboard.r:
rocket_fire.pos = (ship.x,ship.y)
def move_alien(alien):
alien.right +=2
if alien.left > WIDTH:
alien.right = 0
collide = rocket_fire.colliderect(alien)
if collide == 0:
alien.image = 'alien'
elif collide == 1:
alien.image = 'nuclear_explosion'
def update():
rocket_fire.draw()
ship.draw()
alien.draw()
move_ship(ship)
move_alien(alien)
You can try to assign the initial values to 'rocket_fire' after 'collide == 1'.
elif collide == 1:
alien.image = 'nuclear_explosion'
// rocket_fire = (500,400) -> this is just a representation to assign position; not accurate as per the code

Python Crash Course, 2nd edition. Sideways shooter. Aliens keep moving down until the top row hits the bottom

Hello I'm working on getting my sideways shooter game up to speed. but I ran into a problem when I try to move the fleet of aliens down and to the left. I have set the self.rect.bottom >= screen_rect.bottom. but this code is only responsive to my top row of aliens. I tried setting a number value instead but that does not give me the desired result. any help greatly appreciated.
this is my code for the aliens:
alien.py
def check_edges(self):
"""Return True if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.bottom >= screen_rect.bottom or self.rect.top <= 0:
return True
def update(self):
"""Move the alien up or down."""
self.y += (self.settings.alien_speed *
self.settings.fleet_direction)
self.rect.y = self.y
and in alien_invasion.py
def _update_aliens(self):
"""
Check if the fleet is at an edge,
then update the positions of all aliens in the fleet.
"""
self._check_fleet_edges()
self.aliens.update()
def _create_fleet(self):
"""Create the fleet of aliens."""
# Create an alien and find the number of aliens in a row.
# Spacing between each alien is equal to one alien height.
alien = Alien(self)
alien_width = alien.rect.width
alien_height = alien.rect.height
# Determine the number of rows of aliens that fit on the screen.
ship_width = self.ship.rect.width
available_space_x = (self.settings.screen_width -
(2 * alien_width) + ship_width)
number_rows = available_space_x // (3 * alien_width)
available_space_y = self.settings.screen_height + (2 * alien_height)
number_aliens_y = available_space_y // (2 * alien_height)
# Create full fleet of aliens.
for alien_number in range(number_aliens_y):
for row_number in range(number_rows):
self._create_alien(alien_number, row_number)
def _create_alien(self, alien_number, row_number):
"""Create an alien and place it in the row."""
alien = Alien(self)
alien_width = alien.rect.width
alien_height = alien.rect.height
alien.rect.x = 1200 - (2 * alien_width) - (2 * alien.rect.width) * row_number
alien.y = alien_height + 2 * alien_height * alien_number
alien.rect.y = alien.y
self.aliens.add(alien)
def _check_fleet_edges(self):
"""Respond appropriately if any aliens have reached an edge."""
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Drop the entire fleet and change the fleet's direction"""
for alien in self.aliens.sprites():
alien.rect.x -= self.settings.fleet_left_speed
self.settings.fleet_direction *= -1
Your for-loop breaks immediately after the 1st alien.
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
The break statement must be in code block of the if-statement. It's a matter of Indentation
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
I recommend to make your code more robust. If an alien reaches the top, the new fleet direction should be downwards. If an alien reaches the bottom, the new fleet direction should be upwards.
Write a method _set_fleet_direction instead of _change_fleet_direction. The method has a direction parameter that indicates the new direction. The argument has to be 1 for downwards and -1 for upwards. The new fleet_direction can be calculated with the current absolute value (abs(x)) multiplied by the new direction:
def _set_fleet_direction(self, direction):
self.settings.fleet_direction = abs(self.settings.fleet_direction) * direction
Set the direction dependent on the range check:
def _check_fleet_edges(self):
"""Respond appropriately if any aliens have reached an edge."""
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.top <= screen_rect.top:
self._set_fleet_direction(1) # new direction is down
break
elif alien.bottom >= screen_rect.bottom
self._set_fleet_direction(-1) # new direction is up
break
So I stumbled my way to a solution eventually.
It works but I'm not sure it's the best way to do it.
I ended up keeping the
def _change_fleet_direction(self):
should I keep it as it's own helper function or could I included it in the def _set_fleet_direction(self, direction): somehow?
I feel like the game is lagging a bit more then my original alien_invasion game.
Any tips?
This was the solution I ended up with:
def _check_fleet_edges(self):
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.top <= screen_rect.top:
self._set_fleet_direction(1) # new direction is down
break
elif alien.rect.bottom >= screen_rect.bottom:
self._set_fleet_direction(-1) # new direction is up
break
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Moving the aliens left"""
for alien in self.aliens.sprites():
alien.rect.x -= self.settings.fleet_left_speed
def _set_fleet_direction(self, direction):
self.settings.fleet_direction = abs(self.settings.fleet_direction) *
direction

Python Pong Game not increasing point after 1

So I've tried to make a Pong game in Python with turtle, everything is working except one thing. When the player_score reach 1 point its not increasing anymore. And an annoying thing, does anybody know why is the ball slow down when i move the racket?
Here is my code:
I think this part of the code is okay.
from turtle import *
# Creating screen
court = Screen()
court.title("Bricket Pong v 0.2")
court.setup(width=800, height=600)
court.bgcolor('black')
court.tracer(0)
# Creating ball
ball = Turtle()
ball.shape("circle")
ball.color("green")
ball.penup()
ball.setpos(0, 0)
# Creating ball movement speed
def init():
global ball, want_continue
ball.step_x = 0.5
ball.step_y = 0.5
ball.setpos(0, 0)
want_continue = True
def on_quit():
global want_continue
want_continue = False
court.onkey(on_quit, "q")
court.listen()
# Creating point screen
point = Turtle()
point.speed(0)
point.color('blue')
point.penup()
point.hideturtle()
point.goto(0,260)
point.write("Player: 0 ",align="center",font=('Monaco',24,"normal"))
racket = Turtle()
racket.hideturtle()
racket.shape("square")
racket.color("white")
racket.penup()
racket.goto(0, -285)
racket.shapesize(1, 3)
racket.showturtle()
# Creating arrows to move the racket
def racket_left():
x =racket.xcor()
x = x - 15
racket.setx(x)
def racket_right():
x = racket.xcor()
x = x + 15
racket.setx(x)
court.listen()
court.onkeypress(racket_left, "Left")
court.onkeypress(racket_right, "Right")
The problem is must be here in move_ball def.
# Creating borders to the ball
def move_ball():
global ball
player_score = 0
if ball.xcor() > 390 or ball.xcor() < -390:
ball.step_x *= -1
if ball.ycor() > 290:
ball.step_y *= -1
if ball.ycor() < -290:
ball.setpos(0, 0)
ball.step_y *= -1
player_score= 0
point.clear()
point.write("Player: {} ".format(player_score), align="center", font=('Monaco', 24, "normal"))
ball.setx(ball.xcor() + ball.step_x)
ball.sety(ball.ycor() + ball.step_y)
#Racket ball border
if (ball.ycor() < - 265) and ball.ycor() > - 275 \
and (racket.xcor() + 30 > ball.xcor() > racket.xcor() - 30) :
ball.step_y = ball.step_y * -1
player_score += 1
point.clear()
point.write("Player: {}".format(player_score),align="center",font=('Monaco',24,"normal"))
def run():
global ball, want_continue
while want_continue:
move_ball()
court.update()
#
init()
run()
court.bye()
To solve your problem you can define e.g score_a = 0 and score_b = 0 before wherever the collision function is and then update the score every time ball collides.
And you can display the score with formatted strings as print(f"Player A: {score_a} Player B: {score_b}") or print("Player A: {} Player B: {}".format)
The problem of ball slowing down on collision is due to the way of code. The movement of the ball is done by adding some int value to ball's current (x,y) coords. This value is added every time the screen updates. The loop updates the position as many times as it can in one frame. But due to varying CPU speed every second the ball seems to move inconsistently.
To get a more consistent behavior you could add a long enough pause into your loop to offset the variability of the computing times and to avoid drawing useless frames. You can do this using time
import time
# Initialization
...
while True:
time.sleep(1 / 60)
# The rest of your game logic
...
You're continually setting the player_score to zero, at the top of the move_ball() function (so, every cycle through the while loop in run()) -- you'll have to initialize it somewhere else and increment it without resetting it.

Trouble with classes in pygame

I've been working on a game using pygame. So far I have got the shooting to work.
Here is the code:
def game_loop():
global x
global y
x=0
y=0
second_x=0
second_y=0
change_x=0
change_y=0
xlist=[]
ylist=[]
# paused=True
while True:
game_display.blit(background, (0, 0))
mouseclk=pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
x, y = pygame.mouse.get_pos()
xlist.append(x)
ylist.append(y)
if x>=display_width-40:
x=display_width-40
if y>=display_height-48:
y=display_height-48
if event.type == pygame.MOUSEBUTTONUP :
if event.button==1:
bullets.append([x+16,y])
shoot.play()
for bullet in bullets:
game_display.blit(bulletpic, (bullet[0], bullet[1]+10))
for b in range(len(bullets)):
bullets[b][-1] -= 10
for bullet in bullets:
if bullet[1]>display_height:
bullets.remove(bullet)
mainship=Ship(spaceship, x, y, pygame.mouse.get_focused())
mainship.MakeShip()
if pygame.mouse.get_focused()==0:
pause()
pygame.display.update()
clock.tick(60)
The shooting works fine but i really want to use classes in my game. I tried making a class like this:
class Bullet:
def __init__(self, xpos, ypos, sprite, speed, llist):
self.xpos=xpos
self.ypos=ypos
self.sprite=sprite
self.speed=speed
self.list=llist
self.list.append([self.xpos+16,self.ypos])
for bullet in self.list:
game_display.blit(self.sprite, (bullet[0], bullet[1]+10))
for b in range(len(self.list)):
self.list[b][-1] -= self.speed
for bullet in self.list:
if bullet[1]>display_height:
self.list.remove(bullet)
Now, even though this looks like it would work, it just makes the bullets glitchy and slow and sometimes the bullets won't even spawn properly.
Is there any way i can make this work? I'm a noob programmer and new to classes in general so any help will be appreciated.
I think your Bullet does far too much. You could model bullets like so:
class Bullet:
"""This is a bullet. It knows about its position, direction and speed.
It can move. It knows when it crosses the classvariables bounds."""
# bounds as class variable for _all_ bullets - set it to dimensions of your game field
bounds = (0,0,10,10) # if position is lower or higher that these it is out of bounds
# if you got only 1 spite for all bullets (no acid-, fire-, waterbullets) you could
# set it here. I would not, I would leave bullets simply to do the movement and tell
# me if they get out of bounds
def __init__(self, xpos, ypos, xdir, ydir, speed):
# where does the bullet start (f.e. muzzle position)
self.xpos=xpos
self.ypos=ypos
# direction that the bullet flies
self.xdir=xdir
self.ydir=ydir
# initial speed of the bullet
self.speed=speed
def moveBullet(self):
self.xpos += int(self.xdir * self.speed) # speed may be 0.8 or so
self.ypos += int(self.ydir * self.speed)
def outOfBounds(self):
minX,minY,maxX,maxY = Bullet.bounds
return self.xpos < minX or self.ypos < minY or self.xpos > maxX or self.ypos > maxY
def __repr__(self):
"""Pretty print stuff"""
return "({},{}){}".format(self.xpos,self.ypos,"" if
not self.outOfBounds()
else " : Out Of Bounds {}".format(Bullet.bounds))
Your game then sets the bounds of all bullets and keeps track of a list of bullets. If you need a new bullet, simply place it into the list
Bullet.bounds = (0,0,90,90)
bullets = [ Bullet(10,10,12,0,3), Bullet(10,10,11,9,3), Bullet(10,10,0,5,1)]
while bullets:
# you should move this into some kind of `def moveBullets(bulletlist): ...`
# that you then call inside your game loop
for b in bullets:
b.moveBullet()
print(b) # instead draw a spirte at b.xpos, b.ypos
print("")
# return from moveBullets(..)
# remove all out of bounds bullets from list
bullets = filter(lambda b: not b.outOfBounds(),bullets)
Output:
(46,10)
(43,37)
(10,15)
(82,10)
(76,64)
(10,20)
(118,10) : Out Of Bounds (0, 0, 90, 90)
(109,91) : Out Of Bounds (0, 0, 90, 90)
(10,25)
(10,30)
(10,35)
(10,40)
(10,45)
(10,50)
(10,55)
(10,60)
(10,65)
(10,70)
(10,75)
(10,80)
(10,85)
(10,90)
(10,95) : Out Of Bounds (0, 0, 90, 90)

Pygame collision between player and block

I'm making a game and I want to check collision between player and block, and push back player if neccesary.
def collide(self,player):
if self.solid:
if self.rect.colliderect(player.rect):
if self.rect.bottom-1 <= player.rect.top and player.pos == 'up':
player.up = 0
player.rect.move(0,1)
if self.rect.top+1 >= player.rect.bottom and player.pos == 'down':
player.down = 0
player.rect.move_ip(0,-1)
if self.rect.left+1 <= player.rect.right and player.pos == 'right':
player.right = 0
player.rect.move_ip(-1,0)
if self.rect.right-1 >= player.rect.left and player.pos == 'left':
player.left = 0
player.rect.move_ip(1,0)
but for some reason it doesn't work. Can someone help me?
Thanks in advance
Try replacing
def Collide(player,self):
by
def collide(self, player):
if this is a method of some kind of Obstacle class. Just guessing ... :)
If you want your objects to be pushed back, then the easiest way is will involve modifying your update() function just a little bit.
Each time you move, record the current position as "self._oldposition" (or something similar)
When you collide, you will need to set the current position to the old position (self._oldposition)
This will take care of most of your collision problems.

Categories

Resources