I have started making a game in Pygame, and I am currently writing code for an enemy Object. It should choose a random location close (but not on) the player, and move towards that location. I have four images for the enemy to loop through, and if the enemy reaches the location before a random amount of time passes, I want to stay in place and stop looping through its animation. Instead, I want it to stay at the first image.
In my class Enemy, there is a function main that is called during the game loop to update its position on the screen. Here is the code in the function:
def main(self, display):
if self.animation_count + 1 >= 16:
self.animation_count = 0
self.animation_count += 1
if self.reset_offset == 0:
self.offset_x = randrange(-150, 150)
self.offset_y = randrange(-150, 150)
self.reset_offset = randrange(120, 150)
else:
self.reset_offset -= 1
if player.y + self.offset_y > self.y - display_scroll[1]:
self.y += 1
elif player.y + self.offset_x < self.x-display_scroll[1]:
self.y -= 1
if player.x + self.offset_x > self.x - display_scroll[0]:
self.x += 1
if player.y + self.offset_y == self.y - display_scroll[1] and player.x + self.offset_y == self.x - display_scroll[0]:
display.blit(pygame.transform.scale(self.animation_images[0], (32, 42)),
(self.x - display_scroll[0], self.y - display_scroll[1]))
else:
display.blit(pygame.transform.scale(self.animation_images[self.animation_count // 4], (32, 42)),
(self.x - display_scroll[0], self.y - display_scroll[1]))
elif player.x + self.offset_x < self.x-display_scroll[0]:
self.x -= 1
if player.y + self.offset_y == self.y - display_scroll[1] and player.x + self.offset_y == self.x - display_scroll[0]:
display.blit(pygame.transform.scale(self.animation_images[0], (32, 42)),
(self.x - display_scroll[0], self.y - display_scroll[1]))
else:
display.blit(pygame.transform.scale(pygame.transform.flip(
self.animation_images[self.animation_count // 4], True, False),
(32, 42)), (self.x - display_scroll[0], self.y - display_scroll[1]))
When I run it, and the enemy reaches its location before its time is up, it disappears instead. Does anyone know what could be causing this error?
Also, display_scroll is a list I use to draw all other objects. The player remains in the same spot while everything else moves. display_scroll[0] is the x value and display_scroll[1] is the y value.
In addition, I added the line print(self.x - display_scroll[0], self.y - display_scroll[1]) at the end of the function just to see where python was trying to draw the character. It always printed out normal values, nothing out of the ordinary.
the answer is pretty simple:
you are checking
if player.x + self.offset_x > self.x - display_scroll[0]:
[show the image]
elif player.x + self.offset_x < self.x-display_scroll[0]:
[show the image]
so if the enemy is to the right of the player or if it is to the left of the player it will be drawn. but it will not be drawn, if it is on the same x-coordinate as the player.
in order to achieve this you need to add an else statement for this:
else:
display.blit(pygame.transform.scale(self.animation_images[0], (32, 42)),
(self.x - display_scroll[0], self.y - display_scroll[1]))
# or whatever animation you want to play
Related
So I'm creating a little pong game in python (pygame) and I'm trying to create a bot for it...
if you have the balls velocity:
velocity = [10, 5]
the balls x:
x = 300
and the y:
y = 300
is it possible the calculate where the ball is going to go (also knowing where the edges are where the ball bounces)
my code for the game till now:
https://pastebin.com/eQRZedqs
import pygame
import sys
SIZE = (1000, 600)
FRAMES = 60
win = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()
class Player:
def __init__(self, x, y):
self.size = (15, 120)
self.x = x
self.y = y
def draw(self, surface):
pygame.draw.rect(surface, (255, 255, 255),
pygame.Rect((self.x, self.y), self.size))
class Ball:
def __init__(self):
self.x = SIZE[0] // 2
self.y = SIZE[1] // 2
self.vel = [-5, 0]
def move(self):
self.x += self.vel[0]
self.y += self.vel[1]
if self.y <= 20 or self.y >= SIZE[1] - 20:
self.vel[1] *= -1
if (self.x <= 45 and bot.y < self.y < bot.y + bot.size[1]) or (self.x >= SIZE[0] - 45 and p.y < self.y < p.y + p.size[1]):
self.vel[0] *= -1
def draw(self, surface):
pygame.draw.circle(surface, (255, 255, 255), (self.x, self.y), 10)
class Bot(Player):
def __init__(self, x, y):
super().__init__(x, y)
self.ball_vel = b.vel
self.ball_x = b.x
self.ball_y = b.y
def draw_screen(surface):
surface.fill((0, 0, 0))
p.draw(surface)
b.draw(surface)
bot.draw(surface)
b.move()
pygame.display.update()
def key_handler():
keys = pygame.key.get_pressed()
if (keys[pygame.K_UP] or keys[pygame.K_w]) and p.y >= 20:
p.y -= 5
elif (keys[pygame.K_DOWN] or keys[pygame.K_s]) and p.y + p.size[1] <= SIZE[1] - 20:
p.y += 5
def main_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
key_handler()
draw_screen(win)
clock.tick(FRAMES)
if __name__ == "__main__":
pygame.init()
p = Player(SIZE[0] - 45, SIZE[1] // 2 - 60)
b = Ball()
bot = Bot(20, SIZE[1] // 2 - 60)
main_loop()
I'd say add the following function to the Ball class:
class Ball:
def __init__(self):
self.x = SIZE[0] // 2
self.y = SIZE[1] // 2
self.vel = [-5, 0]
def move(self):
self.x += self.vel[0]
self.y += self.vel[1]
if self.y <= 20 or self.y >= SIZE[1] - 20:
self.vel[1] *= -1
if (self.x <= 45 and bot.y < self.y < bot.y + bot.size[1]) or (self.x >= SIZE[0] - 45 and p.y < self.y < p.y + p.size[1]):
self.vel[0] *= -1
def draw(self, surface):
pygame.draw.circle(surface, (255, 255, 255), (self.x, self.y), 10)
# projecting to see in which y the ball will end up (returns the y coordinate)
def project(self):
# setting up local variables as to not change the actual position of the ball
x = self.x
y = self.y
vel = self.vel
# doing practically the same thing as move but with the local x and y
while x > 45 and x < SIZE[0] - 45:
x += vel[0]
y += vel[1]
if y <= 20 or y >= SIZE[1] - 20:
vel[1] *= -1
return y
Since you have the return value of y, then you can get your bot to move directly there, either in a slow manner or an instant one.
If you want to get into quicker solutions, you need to use some physics equations, but in my opinion, this is quick enough (it'll get there pretty quickly). Also, you could alternatively use trigonometry to get the answer, by imagining the velocities as one vector (then you could get the angle of motion relative to the y axis and then use trig to get the final length until the y reaches the border. Repeat that, until you get to the y-axis borders, and you can calculate your x more efficiently (though, once again, there is most probably no need for that, since it should run fast enough).
VIDEO When my player distance is a little farther than the knife they stop shooting at the player I am not sure why? How could I fix it and make sure it shoots where ever the player distance is. Like I don't want it to stop shooting when the player is a little farther away from the projectile I am not sure if I have a range for it to shoot the player or maybe when my player scrolls
when it stops shooting at the player the shooting sound still plays but my projectiles aren't shooting and that causes my sound to keep playing rapidly.
This is how my enemies shoot:
for shootss in shootsright:
shootss.x += shootss.xspeed
shootss.y += shootss.yspeed
if shootss.x > 700 or shootss.x < 0 or shootss.y > 500 or shootss.y < 0:
shootsright.pop(shootsright.index(shootss))
shootss.lookAt((playerman.x,playerman.y))
if box1.health > 25:
if len(shootsright) < 1:
for enemyshoot in enemyshooting:
BULLET_SPEED = 10
start_x = round(enemyshoot.x+enemyshoot.width+-35)
start_y = round(enemyshoot.y + enemyshoot.height+-25)
target_x = playerman.x+playerman.width//2
target_y = playerman.y+playerman.width//2
delta_x, delta_y = target_x - start_x, target_y - start_y
distance = math.sqrt(delta_x ** 2 + delta_y ** 2)
dir_x = BULLET_SPEED * delta_x / distance
dir_y = BULLET_SPEED * delta_y / distance
distance = math.sqrt(dir_x**2 + dir_y**2)
knifesound.play()
if distance > 0:
shootsright.append(enemyboolss(start_x,start_y,(0,0,0),dir_x, dir_y))
This is the class for the projectile:
class projectile(object):
def __init__(self, x, y, dirx, diry, color):
self.x = x
self.y = y
self.dirx = dirx
self.diry = diry
self.isJump = False
self.slash = pygame.image.load("round.png")
self.slash = pygame.transform.scale(self.slash,(self.slash.get_width()//6,self.slash.get_height()//6))
self.rect = self.slash.get_rect()
self.rect.topleft = ( self.x, self.y )
self.speed = 18
self.color = color
self.hitbox = (self.x + -18, self.y, 46,60)
def move(self):
self.x += self.dirx * self.speed
self.y += self.diry * self.speed
def draw(self, window):
self.rect.topleft = (round(self.x), round(self.y))
window.blit(self.slash, self.rect)
self.hitbox = (self.x + -18, self.y, 30,30)
my full code: script
Your screen is 800x800, but the knife code is checking 700x500. The knife projectile is created but immediately removed from the knife list at the next loop:
if shootss.x > 700 or shootss.x < 0 or shootss.y > 500 or shootss.y < 0:
shootsright.pop(shootsright.index(shootss))
shootss.lookAt((playerman.x,playerman.y))
This causes the knife flash but no movement for knives past 700.
After setting the check to 800, the knives worked correctly including sound.
I have an Enemys That Move Left And Right With A HealthBar But How Could I add Particles When The Enemy Dies Like Blood Particles When The enemy Is Killed it appears and falls down and dispears?
Example: >>> VIDEO I killed the enemy and its deleted how could I make load blood particles same position as the enemy that just died
heres my enemy class
class enemys:
def __init__(self,x,y,height,width,end):
self.x = x
self.y =y
self.esright = [pygame.image.load("esright1.png"),
pygame.image.load("esright1.png"),
pygame.image.load("esright2.png"),
pygame.image.load("esright3.png"),
pygame.image.load("esright4.png"),
pygame.image.load("esright5.png"),
pygame.image.load("esright6.png"),
pygame.image.load("esright7.png"),
pygame.image.load("esright8.png"),
pygame.image.load("esright9.png"),
pygame.image.load("esright10.png"),
pygame.image.load("esright11.png"),
pygame.image.load("esright12.png"),
pygame.image.load("esright13.png"),
pygame.image.load("esright14.png"),
pygame.image.load("esright15.png"),
pygame.image.load("esright16.png"),
pygame.image.load("esright17.png"),
]
self.esleft = [pygame.image.load("esleft1.png"),
pygame.image.load("esleft1.png"),
pygame.image.load("esleft2.png"),
pygame.image.load("esleft3.png"),
pygame.image.load("esleft4.png"),
pygame.image.load("esleft5.png"),
pygame.image.load("esleft6.png"),
pygame.image.load("esleft7.png"),
pygame.image.load("esleft8.png"),
pygame.image.load("esleft9.png"),
pygame.image.load("esleft10.png"),
pygame.image.load("esleft11.png"),
pygame.image.load("esleft12.png"),
pygame.image.load("esleft13.png"),
pygame.image.load("esleft14.png"),
pygame.image.load("esleft15.png"),
pygame.image.load("esleft16.png"),
pygame.image.load("esleft17.png"),
]
self.esright = [pygame.transform.scale(image,(image.get_width()//3,image.get_height()//3)) for image in self.esright]
self.esleft = [pygame.transform.scale(image,(image.get_width()//3,image.get_height()//3)) for image in self.esleft]
self.height = height
self.width = width
self.anim_index = 0
self.distance = 80
self.speed = 8
self.vel = 3
self.path = [x,end]
self.Walking_index = 0
self.hitbox = (self.x + 17, self.y + 2, 31, 57)
self.rect = pygame.Rect(x,y,height,width)
COOLDOWN = 30
# enemys health
self.health = 10
self.visible = True
# this makes the enemy move right and left
def draw(self,window):
self.move()
if self.Walking_index + 1 >= 33:
self.Walking_index = 0
if self.vel > 0:
window.blit(self.esright[self.Walking_index//3], (self.x,self.y))
self.Walking_index += 1
else:
window.blit(self.esleft[self.Walking_index//3], (self.x,self.y))
self.Walking_index += 1
# this moves the enemy left and right
def move(self):
if self.visible:
if self.vel > 0:
if self.x + self.vel < self.path[1]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.Walking_index = 0
else:
if self.x - self.vel > self.path[0]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.Walking_index = 0
# the hit box for the enemy the health
pygame.draw.rect(window, (255,0,0), (self.hitbox[0], self.hitbox[1] - 20, 70, 10)) # NEW
pygame.draw.rect(window, (0,255,0), (self.hitbox[0], self.hitbox[1] - 20, 70 - (5 * (10 - self.health)), 10))
self.hitbox = (self.x + 47, self.y + 31, 50, 72)
# THIS PART MAKES the enemy not scroll with the player
def scroll(self,sx, sy):
self.x += sx
self.y += sy
self.path[0] += sx
self.path[1] += sx
heres when the enemys health bar reaches 0 the enemy gets deleted how can I add particles when its deleted? blood particles that drop from the enemys position
# enemys 1
for bullet in bullets:
if bullet.rect.colliderect(enemys2.hitbox):
if enemys2.health > -5:
enemys2.health -= 1
bullets.pop(bullets.index(bullet))
hitesound.play()
# this function calss the -5 text appearing and dispearing on my screen
minusenemyhealthtext()
else:
for oe in range(len(enemyings)-1,-1,-1):
deathsound.play()
del enemyings[oe]
Here are your options:
Get a GIF, split it to multiple images, and just display each of those images one after another. Make all of that in a class.
Make a class (blood class) that would draw make instances of another class (particle class) that just moves outwards and disappears after a while.
I am not going to write the code out for you, you must write it yourself, but that is how you do it.
I have a huge assignment due in class and Im lost like , asking god to spare me lost. Im making a brick game and the most basic function of getting the ball to bounce of the walls I cant figure out , my teacher is too busy and classmates are competitive , I dont want you to write the code for me, I just dont know how to code it. I keep getting errors and its just been hell the last couple of classes.
import math
import pygame
import random
pygame.init()
screen = pygame.display.set_mode([800,600])
done = False
clock = pygame.time.Clock()
#DEFINE COLORS
WHITE = (255,255,255)
BLUE=(0,102,204)
LIGHT_BLUE=(0,0,204)
pink=(238,130,238)
#import images
#lives = pygame.image.load("heart.png")
# initialize font; must be called after 'pygame.init()' to avoid 'Font not Initialized' error
myfont = pygame.font.SysFont("monospace", 15)
#var carying the space of the game
top = -100
left= -50
right= -750
bottom= -600
angle = math.radians(random.randint(-140,-30))
class game_screen():
def draw_screen():
pygame.draw.rect(screen,BLUE,(50,100,700,600))
def save_button():
pygame.draw.rect(screen,pink,(500,20,50,30),0)
save = "Save"
label = myfont.render(save, 40, (0,0,0))
screen.blit(label, (505, 20))
def quit_button():
pygame.draw.rect(screen,pink,(600,20,50,30),0)
quit1 = "Quit"
label = myfont.render(quit1, 40, (0,0,0))
screen.blit(label, (605, 20))
mX, mY = pygame.mouse.get_pos()
mouseButtons = pygame.mouse.get_pressed()
if mouseButtons[0] == True:
if (mX in range (600-20,600+20) and mY in range (20-20,20+20)):
pygame.quit()
#def display_lives():
#lives_counter = screen.blit(lives,(50,30))
#lives_counter2 = screen.blit(lives,(120,30))
#lives_counter3 = screen.blit(lives,(190,30))
class PADDLE:
def __init__(self,xpos,ypos):
self.x = xpos
self.y = ypos
def draw(self): # draws paddle
pygame.draw.rect(screen,pink,(self.x,self.y,70,20))
def move(self):
keys = pygame.key.get_pressed() #checking pressed keys
if keys[pygame.K_LEFT]:
if self.x<=50:
self.x=50
else:
self.x -= 10
elif keys[pygame.K_RIGHT]:
if self.x >=680:
self.x = 680
else:
self.x += 10
class BALL:
def __init__(self,paddle1):
self.x = (paddle1.x+35)
self.y = (paddle1.y-5)
self.speed = 0
self.speedX = 0
self.speedY = 0
self.direction = 200
def draw(self):
pygame.draw.circle(screen,WHITE,(self.x,self.y),10)
def bounce(self):
self.direction = (180 - self.direction) % 360
def move_ball(self):
keys = pygame.key.get_pressed() #checking pressed keys
ball_on_paddle = True
if ball_on_paddle == True :
self.x = (paddle1.x+35)
self.y = (paddle1.y-5)
self.speed = 0
if keys[pygame.K_UP] == True:
ball_on_paddle = False
print("a")
self.speed = 10
self.speedX += int(math.cos(angle)* self.speed)
self.speedY += int(math.sin(angle)* self.speed)
print(bottom)
print(self.speedY)
if self.y <= 0:
self.bounce(0)
self.y = 1
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
paddle1 = PADDLE(350,550)
ball1 = BALL(paddle1)
# MAIN LOOP
while not done:
screen.fill((LIGHT_BLUE))
game_screen.draw_screen()
game_screen.save_button()
game_screen.quit_button()
paddle1.draw()
paddle1.move()
ball1.draw()
ball1.move_ball()
ball1.bounce()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.display.flip()
clock.tick(60)
pygame.quit()
The collission between ball and wall can be detected by comparing the ball's coordinates with the coordinates of the walls. Reflecting the ball means a multiplication of either speedX or speedY - depending on the wall - with -1.
The easiest way to do this is to make a function bounce(self) in your class BALL.
First, you decide which border you touch.
Then, you change the angle of the ball according to which border you bounce on. If you work in a 360-degree system, the angle is mirrored:
around a vertical axis when the ball bounces of the bottom and the top border. So angle = 180 - angle.
around a horizontal axis when the ball bounces of the side borders. So angle = - angle .
And lastly, you have to move the ball back what it has travelled too much.
Like you can see, the ball travels in 1 iteration from C1 to C3', while we want it to go to C3. We can achieve this by reflecting it around the axis BOUNDARY_X - ball_size.
The new x-coordinate of the ball becomes then x = BOUNDARY_X - ball_size - (x - BOUNDARY_X + ball_size) = 2*(BOUNDARY_X - ball_size) - x .
When we cast that into code, we get:
class BALL:
def bounce(self):
if self.x < ball_size:
self.angle = math.pi - self.angle
self.x = 2*ball_size - self.x
elif self.x > DISPLAY_WIDTH - ball_size:
self.angle = math.pi - self.angle
self.x = 2*(DISPLAY_WIDTH - ball_size) - self.x
if self.y < ball_size:
self.angle = - self.angle
self.y = 2*ball_size - self.y
elif self.y > DISPLAY_HEIGHT - ball_size:
self.angle = - self.angle
self.y = 2*(DISPLAY_HEIGHT - ball_size) - self.y
I'm making a simple pong game and I've written the majority of the code.
The problem is, once the ball falls, if you continue to move the paddle, it will bounce back up into the screen from the bottom. I need it to stay off the screen permanently once it misses the paddle.
Any feedback is appreciated! Thanks in advance!
L1_base.py (my base code):
import math
import random
import sys, pygame
from pygame.locals import *
import ball
import colors
import paddle
# draw the scene
def draw(screen, ball1, paddle1) :
screen.fill((128, 128, 128))
ball1.draw_ball(screen)
paddle1.draw_paddle(screen)
#function to start up the main drawing
def main():
pygame.init()
width = 600
height = 600
screen = pygame.display.set_mode((width, height))
ball1 = ball.Ball(300, 1, 40, colors.YELLOW, 0, 4)
paddle1 = paddle.Paddle(200, 575, colors.GREEN, 100, 20)
while 1:
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
paddle1.update_paddle('right', 35)
if event.key == pygame.K_LEFT:
paddle1.update_paddle('left', 35)
ball1.test_collide_top_ball(0)
ball1.test_collide_bottom_ball(600, paddle1)
ball1.update_ball()
draw(screen, ball1, paddle1)
pygame.display.flip()
if __name__ == '__main__':
main()
ball.py (contains ball class/methods):
import pygame
class Ball:
def __init__(self, x, y, radius, color, dx, dy):
self.x = x
self.y = y
self.radius = radius
self.color = color
self.dx = dx
self.dy = dy
def draw_ball(self, screen):
pygame.draw.ellipse(screen, self.color,
pygame.Rect(self.x, self.y, self.radius, self.radius))
def update_ball(self):
self.x += self.dx
self.y += self.dy
def test_collide_top_ball(self, top_height):
if (self.y <= top_height) and (self.dy < 0):
self.dy *= -1
def test_collide_bottom_ball(self, coll_height, paddle):
if (self.y >= coll_height - self.radius - (600 - paddle.y)) and (self.x >= paddle.x) and (self.x <= paddle.x + paddle.width) and (self.dy > 0):
self.dy *= -1
paddle.py (contains paddle class/methods):
import pygame
class Paddle:
def __init__(self, x, y, c, w, h):
self.x = x
self.y = y
self.color = c
self.width = w
self.height = h
def draw_paddle(self, screen):
pygame.draw.rect(screen, self.color,
pygame.Rect(self.x, self.y, self.width, self.height), 0)
def update_paddle(self, dir, dx):
if (dir == 'left'):
self.x = max(self.x - dx, 0)
else:
self.x = min(self.x + dx, 600 - self.width)
def get_left(self):
if (self.x < 300):
return self.x
def get_right(self):
if (self.x >= 300):
return self.x
You tried to cram too much stuff into one if statement in test_collide_bottom_ball and that made debugging much harder. Its also easier in this case to consider all the reasons not to self.dy *= -1 over the reasons you should. That switching of directions is a privilege paddle and should only happen on special occasions.
I have left my debugging code in to clarify the way i thought about the problem, also there is still another bug which can get the ball stuck in the paddle, but you should be able to fix that given a similar approach. Please see the following code:
def test_collide_bottom_ball(self, coll_height, paddle):
print paddle.x, paddle.width + paddle.x, self.x
# shoot ball back to top when it has gone off the bottom
if self.y > 600:
self.y = 300
return
# ignore when the ball is far from the y access of the paddle
if not self.y >= coll_height - self.radius - (600 - paddle.y):
return
# check that the current x plus raidus to find "too small" condition
if self.x + self.radius <= paddle.x:
print "self.x too small"
return
# for too big we have to consider the paddle width
if self.x >= paddle.x + paddle.width:
print "self.x too big"
return
# ok, looks like its time to switch directions!
self.dy *= -1
P.S. use the pep8 coding standard utility. Its not any harder to code following pep8 and will save you headache.