I have a question concerning the colliderect function and variables embedded in classes and functions in general.
I created "hitboxes" around my important elements (player, finish line, monsters), and I want actions to be triggered when those hitboxes "meet".
I understand that the colliderect function from pygame is meant to do that, but somehow I can't implement it.
Could someone explain to me, how I would use the feature in the following code, specifically when the variables I have to call are embedded in separate classes and functions?
Thank you very much, here is my code so far:
#!/usr/bin/python
import sys, pygame, glob, math
from pygame import *
from random import randint
import time
pygame.init()
h = 600
w = 800
screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()
jump_height = 0
class finish_line:
def __init__(self):
self.n = 600
self.b = 200
self.img_finish = pygame.image.load("finish.png")
self.update_finish(0)
def update_finish(self, pos_finish):
screen.blit(self.img_finish, (self.n, self.b))
class player:
def __init__(self):
self.x = 11
self.y = 200
self.speed = 5
self.ani_speed_init = 5
self.ani_speed = self.ani_speed_init
self.ani = glob.glob("animation/run*.png")
self.ani.sort()
self.ani_pos = 0
self.ani_max = len(self.ani)-1
self.img = pygame.image.load(self.ani[0])
self.update(0)
def update(self, pos):
if pos > 0:
if self.x >= (w - 100):
self.x = self.x
else:
self.x += self.speed
self.ani_speed -= 1
if self.ani_speed == 0:
self.img = pygame.image.load(self.ani[self.ani_pos])
self.ani_speed = self.ani_speed_init
if self.ani_pos == self.ani_max:
self.ani_pos = 0
else:
self.ani_pos += 1
elif pos < 0:
if self.x <= 9:
self.x = self.x
else:
self.x -= self.speed
self.ani_speed -= 1
if self.ani_speed == 0:
self.img = pygame.image.load(self.ani[self.ani_pos])
self.ani_speed = self.ani_speed_init
if self.ani_pos == self.ani_max:
self.ani_pos = 0
else:
self.ani_pos += 1
if jump_height == 1:
if self.y <= 10:
self.y = self.y
else:
self.y -= self.speed
elif jump_height == -1:
if self.y >= 500:
self.y = self.y
else:
self.y += self.speed
rectplayer = pygame.draw.rect(screen, (0, 0, 0), ((self.x + 10), (self.y + 15), 65, 70), 1)
screen.blit(self.img, (self.x, self.y))
return self.x, self.y
class monster:
rectmonster = 0
def __init__(self):
self.v = 650
self.c = 200
self.speed_monster = 0.3
self.img_monster = pygame.image.load("orange.png")
self.update_monster(0)
def update_monster(self, pos_monster):
if pos_monster == 1:
self.v = self.v + ((randint(-5, 1)) * self.speed_monster)
self.c = self.c + ((randint(-1, 3)) * self.speed_monster)
if self.v >= 660:
self.v = self.v + ((randint(-2, 0)) * self.speed_monster)
elif self.v <= 140:
self.v = self.v + ((randint(0, 2)) * self.speed_monster)
if self.c <= 140:
self.c = self.c + ((randint(0, 2)) * self.speed_monster)
elif self.c >= 460:
self.c = self.c + ((randint(-2, 0)) * self.speed_monster)
rectmonster = pygame.draw.rect(screen, (0, 0, 0), ((self.v + 12), (self.c + 5), 76, 90), 1)
screen.blit(self.img_monster, (self.v, self.c))
finish1 = finish_line()
player1 = player()
monster1 = monster()
monster2 = monster()
pos = 0
pos_monster = 1
pos_finish = 0
while 1:
screen.fill((255, 204, 229))
pygame.draw.rect(screen, (0,0,0,), (610, 210, 200, 180), 1)
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_RIGHT:
pos = 1
elif event.type == KEYUP and event.key == K_RIGHT:
pos = 0
elif event.type == KEYDOWN and event.key == K_LEFT:
pos = -1
elif event.type == KEYUP and event.key == K_LEFT:
pos = 0
elif event.type == KEYDOWN and event.key == K_UP:
jump_height = 1
elif event.type == KEYUP and event.key == K_UP:
jump_height = 0
elif event.type == KEYDOWN and event.key == K_DOWN:
jump_height = -1
elif event.type == KEYUP and event.key == K_DOWN:
jump_height = 0
finish1.update_finish(pos_finish)
monster1.update_monster(pos_monster)
monster2.update_monster(pos_monster)
player1.update(pos)
pygame.display.flip()
This is my two cents on the problem based of two comments above.
You're assuming pygame.draw.rect automatically gives your class a size property, it does not.
You store the return value of rect() in rectplayer like so:
rectplayer = pygame.draw.rect(...)
And I bet rect() returns None to begin with. Besides that the variable isn't stored as self.rectplayer so it's a local function variable not a class-owned variable - so Pygame can't check against it.
This is an easy mistake to make and if that was the only root to the problem here, it would be an easy fix.
Unfortunately there's more assumptions here, such as the fact that colliderrect is a global function of Pygame.
colliderect & What is sprites?
There's a key note on this internal function of Pygame that states:
all sprites must have a “rect” value, which is a rectangle of the
sprite area, which will be used to calculate the collision.
It's a internal function called by pygame.sprite.spritecollide() that mentions the fact that you should be using Sprites.
Here's a minimal example of a sprite object:
class Player(pygame.sprite.Sprite):
def __init__(self, color, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
At the bottom of the initialization process of this sprite we define our rectangle self.rect that contains our (start_x, start_y), (width, height) data. These are size properties that you haven't defined in your above example.
Most likely, you could define something along the lines of:
class player:
def __init__(self):
self.rect = ...
But remember, your <class player> has no function called spritecollide() or collide_rect(), these functions is a inheritance either from pygame.sprite.Sprite or pygame.Surface, so you're better off sticking to these as your inherited object for your custom classes.
See my above example of class Player(...) how to do that.
I know this doesn't solve your entire code base, but hopefully it will give you some sense or idea of what's needed to complete your code.
End note
A friendly reminder, if you ever think that something is done for you - double check it. Assuming a class gets size properties because you drew a rectangle, check if your class actually did.
player1 = player()
print(dir(player1))
Is there anything in there remotely resembling width, height, size or rectangle? If not, this should indicate where something is funky.
Related
This question already has an answer here:
Replace a rectangle with an Image in Pygame
(1 answer)
Closed last year.
So I don't know how to turn the rectangle representing the player into an image (spaceship). I know it must be simple, but after a few hours with this I'm a little frustrated, so I'm looking for help :(
bg_color = pygame.Color('black')
text_color = pygame.Color('darkgrey')
obstacles_color = pygame.Color('darkred')
player_color = pygame.Color('yellow')
fps = 60
window_height = 1200
window_width = 1200
player_speed = 4
player_size = 8
player_max_up = 125
obstacle_spawn_rate = 1
obstacle_min_size = 3
obstacle_max_size = 6
obstacle_min_speed = 2
obstacle_max_speed = 2
class Player:
def __init__(self):
self.size = player_size
self.speed = player_speed
self.color = player_color
self.position = (window_width / 2, (window_height - (window_height / 10)))
def draw(self, surface):
r = self.get_rect()
pygame.draw.rect(surface, self.color, r)
def move(self, x, y):
newX = self.position[0] + x
newY = self.position[1] + y
if newX < 0 or newX > window_width - player_size:
newX = self.position[0]
if newY < window_height - player_max_up or newY > window_height - player_size:
newY = self.position[0]
self.position = (newX, newY)
def collision_detection(self, rect):
r = self.get_rect()
return r.colliderect(rect)
def get_rect(self):
return pygame.Rect(self.position, (self.size, self.size))
class Obstacles:
def __init__(self):
self.size = random.randint(obstacle_min_size, obstacle_max_size)
self.speed = random.randint(obstacle_min_speed, obstacle_max_speed)
self.color = obstacles_color
self.position = (random.randint(0, window_width - self.size), 0 - self.size)
def draw(self, surface):
r = self.get_rect()
pygame.draw.rect(surface, self.color, r)
def move(self):
self.position = (self.position[0], self.position[1] + self.speed)
def is_off_window(self):
return self.position[1] > window_height
def get_rect(self):
return pygame.Rect(self.position, (self.size, self.size))
class World:
def __init__(self):
self.reset()
def reset(self):
self.player = Player()
self.obstacles = []
self.gameOver = False
self.score = 0
self.obstacles_counter = 0
self.moveUp = False
self.moveDown = False
self.moveLeft = False
self.moveRight = False
def is_game_over(self):
return self.gameOver
def update(self):
self.score += 1
if self.moveUp:
self.player.move(0, - player_speed)
if self.moveUp:
self.player.move(0, player_speed)
if self.moveLeft:
self.player.move(-player_speed, 0)
if self.moveRight:
self.player.move(player_speed, 0)
for each in self.obstacles:
each.move()
if self.player.collision_detection(each.get_rect()):
self.gameOver = True
if each.is_off_window():
self.obstacles.remove(each)
self.obstacles_counter += 1
if self.obstacles_counter > obstacle_spawn_rate:
self.obstacles_counter = 0
self.obstacles.append(Obstacles())
def draw(self, surface):
self.player.draw(surface)
for each in self.obstacles:
each.draw(surface)
def movement_keys(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.moveUp = True
if event.key == pygame.K_DOWN:
self.moveDown = True
if event.key == pygame.K_LEFT:
self.moveLeft = True
if event.key == pygame.K_RIGHT:
self.moveRight = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
self.moveUp = False
if event.key == pygame.K_DOWN:
self.moveDown = False
if event.key == pygame.K_LEFT:
self.moveLeft = False
if event.key == pygame.K_RIGHT:
self.moveRight = False
def run():
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode((window_height, window_width))
pygame.display.set_caption("Avoid Obstacles")
surface = pygame.Surface(window.get_size())
surface = surface.convert()
world = World()
font = pygame.font.SysFont("Times", 35, "bold")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == ord("r"):
world.reset()
else:
world.movement_keys(event)
if not world.is_game_over():
world.update()
window.fill(bg_color)
clock.tick(fps)
world.draw(window)
surface.blit(surface, (0, 0))
text = font.render("Score {0}".format(world.score), 1, text_color)
window.blit(text, (1, 1))
if world.is_game_over():
end = font.render("The end of your journey", 1, text_color)
window.blit(end, (window_width / 2 - 220, window_height / 2))
restart = font.render("Hit R to reset", 1, text_color)
window.blit(restart, (window_width / 2 - 120, window_height / 2 + 45))
pygame.display.update()
if __name__ == '__main__':
run()
pygame.quit()
I tried changing the draw function in the Player class, but still couldn't figure out how to do it correctly. I'm drawing an image of the spaceship on the screen/window, but I can't get it to move to the player rectangle.
I don't know what you've tried, but here's how to do this with a call to blit:
myimage = pygame.image.load("bar.png")
...
def draw(self, surface):
r = self.get_rect()
surface.blit(myimage, r)
The key point here is that you're giving blit the same coordinates that you were using to draw the rectangle. When I tried this, I got the correct/natural size of the image even though the r rectangle is of different dimensions. If that turns out to cause a problem, you could adjust the dimensions of the rectangle to be the same as the image. You also might want to adjust the coordinates of r such that the image is drawn centered on the location at which you wish to draw it.
The 'draw' family of functions are convenience functions that create Surface objects and then paint pixels on it that correspond to basic shapes (like circles, rectangles etc, filled or otherwise).
Instead, you need to create a Surface where the pixels have been initialised via an image. For this, you need the "image" family of functions (see https://www.pygame.org/docs/ref/image.html for image functions).
Other than that, everything in pygame is a surface, and you're manipulating and updating surfaces on the display, so your code should not change, whether your surfaces were initialised via 'draw' or 'image'.
When I run my code the program says to me:
Traceback (most recent call last):
File "C:\Users\User\Documents\projects\two_player_gun_game.py", line 192, in <module>
if player_one_bullet.is_collided_with(player_two):
NameError: name 'player_one_bullet' is not defined
I do not understand why this reason comes up I have created a function in one of the classes which is is_collided_with but. it still seems not to work can. I have put at the bottom an if statement which checks for collisions. colllisions are meant to be happening for player 1 and 2's bullets.
Here is my code to help:
import pygame
import random
import sys
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock() # A clock to limit the frame rate.
pygame.display.set_caption("this game")
class Background:
picture = pygame.image.load("C:/images/space.jpg").convert()
picture = pygame.transform.scale(picture, (1280, 720))
def __init__(self, x, y):
self.xpos = x
self.ypos = y
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
class player_first:
picture = pygame.image.load("C:/aliens/ezgif.com-crop.gif")
picture = pygame.transform.scale(picture, (200, 200))
def __init__(self, x, y):
self.xpos = x
self.ypos = y
self.speed_x = 0
self.speed_y = 0
self.rect = self.picture.get_rect()
def update(self):
self.xpos += self.speed_x
self.ypos += self.speed_y
def draw(self): #left right
#screen.blit(pygame.transform.flip(self.picture, True, False), self.rect)
screen.blit(self.picture, (self.xpos, self.ypos))
class player_second:
picture = pygame.image.load("C:/aliens/Giantmechanicalcrab2 - Copy.gif")
picture = pygame.transform.scale(picture, (300, 200))
def __init__(self, x, y):
self.xpos = x
self.ypos = y
self.speed_x = 0
self.speed_y = 0
self.rect = self.picture.get_rect()
def update(self):
self.xpos += self.speed_x
self.ypos += self.speed_y
def draw(self): #left right
#screen.blit(pygame.transform.flip(self.picture, True, False), self.rect)
screen.blit(self.picture, (self.xpos, self.ypos))
class player_one_Bullet(pygame.sprite.Sprite):
picture = pygame.image.load("C:/aliens/giphy.gif").convert_alpha()
picture = pygame.transform.scale(picture, (100, 100))
def __init__(self):
self.xpos = 360
self.ypos = 360
self.speed_x = 0
super().__init__()
self.rect = self.picture.get_rect()
def update(self):
self.xpos += self.speed_x
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
#self.screen.blit(pygame.transform.flip(self.picture, False, True), self.rect)
def is_collided_with(self, sprite):
return self.rect.colliderect(sprite.rect)
class player_two_Bullet(pygame.sprite.Sprite):
picture = pygame.image.load("C:/aliens/MajesticLavishBackswimmer-size_restricted.gif").convert_alpha()
picture = pygame.transform.scale(picture, (100, 100))
picture = pygame.transform.rotate(picture, 180)
def __init__(self):
self.xpos = 360
self.ypos = 360
self.speed_x = 0
super().__init__()
self.rect = self.picture.get_rect()
def update(self):
self.xpos -= self.speed_x
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
#self.screen.blit(pygame.transform.flip(self.picture, False, True), self.rect)
def is_collided_with(self, sprite):
return self.rect.colliderect(sprite.rect)
player_one = player_first(0, 0)
player_two = player_second(1000, 0)
cliff = Background(0, 0)
player_one_bullet_list = pygame.sprite.Group()
player_two_bullet_list = pygame.sprite.Group()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player_one.speed_y = -5
elif event.key == pygame.K_s:
player_one.speed_y = 5
elif event.key == pygame.K_UP:
player_two.speed_y = -5
elif event.key == pygame.K_DOWN:
player_two.speed_y = 5
elif event.key == pygame.K_SPACE:
player_one_bullet = player_one_Bullet()
player_one_bullet.ypos = player_one.ypos
player_one_bullet.xpos = player_one.xpos
player_one_bullet.speed_x = 14
player_one_bullet_list.add(player_one_bullet)
elif event.key == pygame.K_KP0:
player_two_bullet = player_two_Bullet()
player_two_bullet.ypos = player_two.ypos
player_two_bullet.xpos = player_two.xpos
player_two_bullet.speed_x = 14
player_two_bullet_list.add(player_two_bullet)
elif event.type == pygame.KEYUP:
# Stop moving when the keys are released.
if event.key == pygame.K_s and player_one.speed_y > 0:
player_one.speed_y = 0
elif event.key == pygame.K_w and player_one.speed_y < 0:
player_one.speed_y = 0
if event.key == pygame.K_DOWN and player_two.speed_y > 0:
player_two.speed_y = 0
elif event.key == pygame.K_UP and player_two.speed_y < 0:
player_two.speed_y = 0
if player_one_bullet.is_collided_with(player_two):
player_one_bullet.kill()
if player_two_bullet.is_collided_with(player_one):
player_two_bullet.kill()
player_one.update()
player_two.update()
cliff.draw()
player_one.draw()
player_two.draw()
player_one_bullet_list.update()
player_two_bullet_list.update()
for player_one_bullet in player_one_bullet_list:
player_one_bullet.draw()
for player_two_bullet in player_two_bullet_list:
player_two_bullet.draw()
pygame.display.flip()
clock.tick(60)
#IGNORE THIS
#player_one_bullet_list = pygame.sprite.Group()
#elif event.key == pygame.K_SPACE:
# player_one_bullet = player_one_Bullet()
#
# player_one_bullet.ypos = player_one.ypos
# player_one.xpos = player_one.xpos
#
# player_one_bullet.speed_x = 14
#
# player_one_bullet_list.add(player_one_bullet)
#
#
#player_one_bullet_list.update()
#
# for player_one_bullet in player_one_bullet_list:
# player_one_bullet.draw()
#if hammerhood.rect.colliderect(crab): #.rect.top
# hammerhood.speed_y = 0
When python complains about something not being defined, it typically means you are trying to use something which is not there or has not yet been made.
This is the only time you define 'player_one_bullet':
elif event.key == pygame.K_SPACE:
player_one_bullet = player_one_Bullet()`
So long as you do not press space, there are no bullets and 'player_one_bullet' remains undefined.
That is the reason why this piece of code gives an error:
if player_one_bullet.is_collided_with(player_two):
You cannot use something which does not exist!
Additionally only the last bullet fired from each player will check if it is hitting the other player, the other bullets will ignore everything!
Fortunately there is an easy way to fix it; instead of doing this:
if player_one_bullet.is_collided_with(player_two):
player_one_bullet.kill()
if player_two_bullet.is_collided_with(player_one):
player_two_bullet.kill()
You can use player_one_bullet_list and player_two_bullet_list to check if bullets exist and whether they are colliding!
for bullet in player_one_bullet_list.sprites():
if bullet.is_collided_with(player_two):
player_one_bullet.kill()
for bullet in player_two_bullet_list.sprites():
if bullet.is_collided_with(player_one):
player_two_bullet.kill()
Now you have a for loop that iterates through the list of bullets and if any exist only then it checks whether there is a collision.
Hope this helps you out!
import pygame, sys, random
pygame.init()
class Ship(pygame.sprite.Sprite):
movepersec = 0
dx = 0
dy = 0
direction = ""
imgarray = {}
def __init__(self, imgarr, rect, speed, xpos, ypos, direct):
pygame.sprite.Sprite.__init__(self)
self.imgarray = imgarr
self.rect = rect
self.movepersec = speed
self.dx = xpos
self.dy = ypos
self.direction = direct
self.image = self.imgarray[self.direction]
def setDirection(self, direct):
self.direction = direct
def setSpeed(self, speed):
self.movepersec = speed
def update(self, secs):
movePix = self.movepersec*secs
if self.direction == "N": self.dy -= movePix
if self.direction == "S": self.dy += movePix
if self.direction == "E": self.dx += movePix
if self.direction == "W": self.dx -= movePix
self.rect.centerx = self.dx
self.rect.centery = self.dy
self.image = self.imgarray[self.direction]
background = pygame.image.load("sea.jpg")
backgroundWidth = background.get_width()
backgroundHeight = background.get_height()
size = background.get_size()
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
imgarray = {}
imgarray["N"] = pygame.image.load("shipNorth.png")
imgarray["S"] = pygame.image.load("shipSouth.png")
imgarray["E"] = pygame.image.load("shipEast.png")
imgarray["W"] = pygame.image.load("shipWest.png")
imgrect = imgarray["N"].get_rect()
movepersec = 150
keydownflag = False
allSpriteGroup = pygame.sprite.Group()
shipX = Ship(imgarray, imgrect, movepersec, 100, 100, "E")
allSpriteGroup.add(shipX)
shipY = Ship(imgarray, imgrect, movepersec, 100, 100, "S")
allSpriteGroup.add(shipY)
screen.blit(background,(0,0))
while True:
secs = clock.tick(30)/1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT : sys.exit(0)
if event.type == pygame.KEYDOWN:
keydownflag = True
if event.key == pygame.K_LEFT:
shipX.setDirection("W")
if event.key == pygame.K_RIGHT:
shipX.setDirection("E")
if event.key == pygame.K_UP:
shipX.setDirection("N")
if event.key == pygame.K_DOWN:
shipX.setDirection("S")
if event.type == pygame.KEYUP:
keydownflag = False
if keydownflag:
shipX.update(secs)
shipY.update(secs)
#shipX collision
if shipX.rect.right + 65 >= backgroundWidth:
shipX.setDirection("W")
if shipX.rect.bottom >= backgroundHeight:
shipX.setDirection("N")
if shipX.rect.left <= 0:
shipX.setDirection("E")
if shipX.rect.top <= 0:
shipX.setDirection("S")
#Ship Y collision
if shipY.rect.top <= 0:
shipY.setDirection("S")
if shipY.rect.bottom >= backgroundHeight:
shipY.setDirection("N")
print(shipX.dx)
allSpriteGroup.clear(screen,background)
allSpriteGroup.draw(screen)
pygame.display.flip()
Quick rundown, shipX should move when the arrow keys are pressed and shipY moves up and down by itself. Im having an issue where the sprites update only to shipY meaning they will only move up and down. the ShipX sprite will rotate properly and the x and y locations change in the log, but the sprites seem to constantly be attached. I'm stumped and cannot figure out a way to fix this issue. Any help would be great. thank you
To solve the problem change the __init__() method of your Ship sprite class to:
def __init__(self, imgarr, speed, xpos, ypos, direct):
pygame.sprite.Sprite.__init__(self)
self.imgarray = imgarr
self.movepersec = speed
self.dx = xpos
self.dy = ypos
self.direction = direct
self.image = self.imgarray[self.direction]
self.rect = self.image.get_rect() #create a *new* rect-object
The important thing is that every time you create a new instance (i.e. a new sprite) you must also create a new pygem.rect object (see above code).
In your code every Ship object (shipX and shipY) used the same pygem.rect object which was passed to it via the __init__() method (rect argument).
When you called the update() method, one of the two ships changed the rect.centerx and rect.centery attributes of the same rect. That´s the reason why PyGame drew both sprites on the same (rect)-position (allSpriteGroup.draw(screen)).
When you create now a new ship, be aware that the constructor (__init__() method) does not need the rect argument anymore:
shipX = Ship(imgarray, movepersec, 100, 100, "E")
By default shipX appears in the top left corner, because the .get_rect() method returns a pygame.rect which location is set to (0, 0). To set the position of shipX according to the passed xpos and ypos arguments in the constructor, add these two lines after the craetion of the self.rect object:
self.rect.centerx = self.dx
self.rect.centery = self.dy
I hope this helps :)
My character won't jump properly, he keeps glitching through the ground. If i hold key up, my character won't stop going higher. Do you have some ideas for how I could fix it?
import pygame,sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1200, 800))
screen.fill((0,0,0))
pygame.display.set_caption('Jump and Run')
BLACK = (0,0,0)
WHITE = (250, 250,250)
RED = (250, 0, 0)
BLUE = (0,0,250)
direction = 'right'
way = 0
jump_high = 0
class Hero():
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load('hero.bmp')
self.groundcontact = True
self.vy = 0
alive = True
def check_pos(self):
if self.y >= 400:
self.groundcontact = True
elif self.y <= 400:
self.groundcontact = False
def load_picture(self,surface):
surface.blit(self.image,(self.x, self.y))
def check_input(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.x += 10
elif key[pygame.K_LEFT]:
self.x -= 10
elif key[pygame.K_UP]:
self.y -= 50
if not self.groundcontact:
self.vy += 1 #max(min(2,200), -200)
#self.y += self.vy
print "not self.groundcontact"
else:
self.vy = 0
#self.y += self.vy
self.y += self.vy
class Monster():
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load('monster.bmp')
self.collision = False
self.alive = True
def load_picture(self,surface):
surface.blit(self.image,(self.x, self.y))
def walk(self):
global direction
global way
if direction == "right":
self.x += 4
way += 1
if way == 100:
direction = "left"
elif direction == "left":
self.x -= 4
way -= 1
if way == 0:
direction = "right"
monster2 = Monster( 200, 333)
monster1 = Monster(400, 450)
hero = Hero(0, 400)
clock = pygame.time.Clock()
pygame.draw.rect(screen, WHITE,(0,500, 1200, 50))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
hero.check_pos()
monster1.walk()
monster2.walk()
hero.check_input()
screen.fill(BLACK)
hero.load_picture(screen)
monster1.load_picture(screen)
monster2.load_picture(screen)
pygame.draw.rect(screen, WHITE,(0,500, 1200, 50))
pygame.display.update()
clock.tick(40)
Regarding my comment above, changing the Hero class to somthing like this:
JUMP_POWER = 10
class Hero():
...
def check_input(self):
key = pygame.key.get_pressed()
...
elif key[pygame.K_UP]:
if self.groundcontact:
self.vy -= JUMP_SPEED
I'm making a program that clones pong based off a tutorial and I already have the program to where it is multiplayer with two separate people. I want to add an AI in the program instead of a player 2. I've been stuck on this for quite some time and would appreciate any help! Here is the code currently:
import sys, os, math, random, pygame
from pygame.locals import *
class paddle(pygame.sprite.Sprite):
def __init__(self, xy):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('assets', 'pong_paddle.gif'))
self.rect = self.image.get_rect()
self.rect.centerx, self.rect.centery = xy
self.movementspeed = 5
self.velocity = 0
def up(self):
# increases vertical velocity
self.velocity -= self.movementspeed
def down(self):
# decreases vertical velocity
self.velocity += self.movementspeed
def move(self, dy):
# moves the paddle y, doesn't go out of top or bottom
if self.rect.bottom + dy > 400:
self.rect.bottom = 400
elif self.rect.top + dy < 0:
self.rect.top = 0
else:
self.rect.y += dy
def update(self):
# makes the paddle move every frame
self.move(self.velocity)
class aiplayer(object):
def __init__(self):
self.bias = random.random() - 0.5
self.hit_count = 0
def update(self, paddle, game,):
if (paddle.rect.centerx < game.bounds.centerx and game.ball.rect.centerx < game.bounds.centerx) or (paddle.rect.centerx > game.bounds.centerx and game.ball.rect.centerx > game.bounds.centerx):
delta = (paddle.rect.centery + self.bias * paddle.rect.height) - game.ball.rect.centery
if abs(delta) > paddle.velocity:
if delta > 0:
paddle.direction = -1
else:
paddle.direction = 1
else:
paddle.direction = 0
else:
paddle.direction = 0
def hit(self):
self.hit_count += 1
if self.hit_count > 6:
self.bias = random.random() - 0.5
self.hit_count = 0
def lost(self):
self.bias = random.random() - 0.5
def won(self):
pass
def render(self, surface):
x, y = self.location
w, h = self.image.get_size()
surface.blitz(self.image, (x-w/2, y-h/2))
class Ball(pygame.sprite.Sprite):
def __init__(self, xy):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('assets', 'pong_ball.gif'))
self.rect = self.image.get_rect()
self.rect.centerx, self.rect.centery = xy
self.maxspeed = 10
self.servespeed = 5
self.velx = 0
self.vely = 0
def reset(self):
self.rect.centerx, self.rect.centery = 400, 200
self.velx = 0
self.vely = 0
def serve(self):
angle = random.randint(-45, 45)
if abs(angle) < 5 or abs(angle-180) < 5:
angle = random.randint(10, 20)
if random.random() > .5:
angle += 180
# this gets the velocity for the x and y coords
x = math.cos(math.radians(angle))
y = math.sin(math.radians(angle))
self.velx = self.servespeed * x
self.vely = self.servespeed * y
class Game(object):
def __init__(self):
pygame.init()
# creates the window
self.window = pygame.display.set_mode((800, 400))
# makes a clock
self.clock = pygame.time.Clock()
# window title
pygame.display.set_caption("Pong")
# tells pygame to watch for these certain events so we can close window
pygame.event.set_allowed([QUIT, KEYDOWN, KEYUP])
self.background = pygame.Surface((800, 400))
self.background.fill((55, 255, 85))
pygame.draw.line(self.background, (0,0,0), (400, 0), (400, 400), 2)
self.window.blit(self.background, (0,0))
#lets the background show up
pygame.display.flip()
#renders the sprites so that they actually show up
self.sprites = pygame.sprite.RenderUpdates()
# makes the paddles, adds to sprite group
self.leftpaddle = paddle((50, 200))
self.sprites.add(self.leftpaddle)
self.rightpaddle = paddle((750, 200))
self.sprites.add(self.rightpaddle)
# makes the ball
self.ball = Ball((400, 200))
self.sprites.add(self.ball)
def run(self):
# this lets the game run using a loop so its always active and never closes
running = True
while running:
self.clock.tick(60)
# pygame event, if user closes the game, then stop running
running = self.handleEvents()
pygame.display.set_caption("Pong %d fps" % self.clock.get_fps())
self.manageBall()
# updates the sprites(paddles, ball)
for sprite in self.sprites:
sprite.update()
# renders the sprites
self.sprites.clear(self.window, self.background)
dirty = self.sprites.draw(self.window)
pygame.display.update(dirty)
def handleEvents(self):
for event in pygame.event.get():
if event.type == QUIT:
return False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
return False
# controls the right paddle
if event.key == K_w:
self.leftpaddle.up()
if event.key == K_s:
self.leftpaddle.down()
if event.key == K_UP:
self.rightpaddle.up()
if event.key == K_DOWN:
self.rightpaddle.down()
# serves the ball
if event.key == K_SPACE:
if self.ball.velx == 0 and self.ball.vely == 0:
self.ball.serve()
elif event.type == KEYUP:
if event.key == K_w:
self.leftpaddle.down()
if event.key == K_s:
self.leftpaddle.up()
if event.key == K_UP:
self.rightpaddle.down()
if event.key == K_DOWN:
self.rightpaddle.up()
elif event.type ==
return True
def manageBall(self):
# this moves the ball
self.ball.rect.x += self.ball.velx
self.ball.rect.y += self.ball.vely
if self.ball.rect.top < 0:
self.ball.rect.top = 1
# makes the ball bounce
self.ball.vely *= -1
elif self.ball.rect.bottom > 400:
self.ball.rect.bottom = 399
# makes ball bounce off bottom
self.ball.vely *= -1
# resets the ball if it hits the left or right screen
if self.ball.rect.left < 0:
self.ball.reset()
return
elif self.ball.rect.right > 800:
self.ball.reset()
return
collision = pygame.sprite.spritecollide(self.ball, [self.leftpaddle, self.rightpaddle], dokill = False)
if len(collision) > 0:
hitpaddle = collision[0]
# sends the ball back
self.ball.velx *= -1
# makes sure the ball doesn't get stuck in the paddle
self.ball.rect.x += self.ball.velx
# makes the game and runs it
if __name__ == '__main__':
game = Game()
game.run()
Make a function AI in the aiplayer and have it return up or down
int AI(self.position,ball.position):
if self.y>ball.y:
return -1
elif self.y<ball.y:
return 1
then, in the update() code for aiplayer, do something similar to this
self.y += movespeed*AI(self.position,ball.position)
Then, it either moves the paddle up or down depending on where the ball is ( you might need to switch if you add or subtract the movespeed to get the paddle to go the right direction). Also, it might be more effective to use the center of the paddle so that it won't put the top or bottom edge of the paddle at the ball.