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'.
Related
So i have a problem where my character is drawn at the first frame and i am able to move and collect "ammo" with it but my character is invisible and i have no idea why it is happening.
Also tried to change something from Vector to rect but it does not works.
import pygame
from sys import exit
from pygame.math import Vector2
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Rectshooter")
clock = pygame.time.Clock()
game_active = True
class Player :
width = height = 50
def __init__(self,color=(255,0,0)):
self.body = Vector2(500,200)
self.color = color
self.direction = Vector2(0,0)
self.player_rect = pygame.Rect(self.body.x,self.body.y,self.width,self.height)
def draw(self):
#pygame.draw.rect(win,self.color,(self.body.x,self.body.y,self.width,self.height))
pygame.draw.rect(win, self.color, self.player_rect)
def move(self):
self.body += self.direction
class Player2 :
width = height = 50
def __init__(self,color=(255,0,0)):
self.body = Vector2(300,200)
self.color = color
self.direction = Vector2(0,0)
def draw(self):
pygame.draw.rect(win,self.color,(self.body.x,self.body.y,self.width,self.height))
def move(self):
self.body += self.direction
class Ammo:
width = height = 30
def __init__(self,x1,x2):
self.x = random.randint(x1,x2)
self.y = random.randint(50,550)
self.pos = Vector2(self.x,self.y)
self.ammo_rect = pygame.Rect(self.x,self.y,self.width,self.height)
def draw_ammo(self):
pygame.draw.ellipse(win,(255,100,00),self.ammo_rect)
player1 = Player()
player2 = Player2()
speed = 15 #player speed
ammo1 = Ammo(50,300)
ammo2 = Ammo(450,750)
print(ammo2.pos.x)
print(player1.body.x)
There is another problem with colliding, i wrote a long if statement for it and it works fine,but i want to make it with simple "player1.player_rect.colliderect(ammo2.ammo_rect)" if statement,but this only triggers when the ammo(random spawn) spwans at the location of the player,i mean the original location of the player that spawns at the first frame not the current location.
I am so confused,and i need a little help with it.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
#player1 key pressing movement
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
player1.direction = Vector2(0,-speed)
if event.key == pygame.K_DOWN:
player1.direction = Vector2(0,speed)
if event.key == pygame.K_LEFT:
player1.direction = Vector2(-speed,0)
if event.key == pygame.K_RIGHT:
player1.direction = Vector2(speed,0)
#player2 key pressing movement
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player2.direction = Vector2(0,-speed)
if event.key == pygame.K_s:
player2.direction = Vector2(0,speed)
if event.key == pygame.K_a:
player2.direction = Vector2(-speed,0)
if event.key == pygame.K_d:
player2.direction = Vector2(speed,0)
if game_active:
#player1 preventing from going out of the screen and the middle line
if player1.body.y < 0:#up
player1.body.y = 0
player1.direction = Vector2(0,0)
if player1.body.x < 410:#left
player1.body.x = 410
player1.direction = Vector2(0,0)
if player1.body.y > 550:#down
player1.body.y = 550
player1.direction = Vector2(0,0)
if player1.body.x > 750:#right
player1.body.x = 750
player1.direction = Vector2(0,0)
#player2 preventing from going out of the screen and the middle line
if player2.body.y < 0:#up
player2.body.y = 0
player2.direction = Vector2(0,0)
if player2.body.x < 0:#left
player2.body.x = 0
player2.direction = Vector2(0,0)
if player2.body.y > 550:#down
player2.body.y = 550
player2.direction = Vector2(0,0)
if player2.body.x > 341:#right
player2.body.x = 341
player2.direction = Vector2(0,0)
#collide p1 with ammo1
if ammo2.pos.x + 20 > player1.body.x - 20 and ammo2.pos.x - 20 < player1.body.x + 20 and ammo2.pos.y + 20 > player1.body.y - 20 and ammo2.pos.y - 20 < player1.body.y + 20:
ammo2 = Ammo(450,750)
ammo2.draw_ammo()
print("collide")
if player1.player_rect.colliderect(ammo2.ammo_rect):
print("collide with start position")
#print(player1.body.x)
#print(player1.body.y)
win.fill((0, 0, 0))
pygame.draw.line(win, (255, 255, 255), (400, 600), (400, 0), 20)
#p1
player1.draw()
player1.move()
#p2
player2.draw()
player2.move()
#ammo
ammo1.draw_ammo()
ammo2.draw_ammo()
pygame.display.update()
clock.tick(60)
The player is drawn at the position stored in the player_rect attribute. When you change the body attribute, the position of the rectangle does not magically change. You need to update the position of the rectangle:
class Player :
width = height = 50
def __init__(self,color = (255,0,0)):
self.body = Vector2(500, 200)
self.color = color
self.direction = Vector2(0, 0)
self.player_rect = pygame.Rect(self.body.x, self.body.y, self.width, self.height)
def draw(self):
pygame.draw.rect(win, self.color, self.player_rect)
def move(self):
self.body += self.direction
# update player_rect
self.player_rect.x = round(self.body.x)
self.player_rect.y = round(self.body.y)
I rotate an image and I adjust the rect to the rotated image. When I start the program, the image starts moving, without any order by the program. There are, for testing, some pictures needed. I hope, as more experienced users, can take some standards.
import pygame
import sys
from pygame.constants import KEYUP
from pygame.constants import KEYDOWN
import math
import random
pygame.init()
cell_size = 40
cell_number = 20
breite = int(cell_size * cell_number )
hoehe = int( cell_size * cell_number )
screen = pygame.display.set_mode((breite,hoehe))
clock = pygame.time.Clock()
anzahl_gegenstaende = 10 # gehört zu landschaft
class MeinAuto(pygame.sprite.Sprite):
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(auto_img,(30,66)).convert_alpha()
self.rect = self.image.get_rect(center=(x,y))
self.rect.x = x
self.rect.y = y
self.mask = pygame.mask.from_surface(self.image )
self.wagen_winkel = 0
self.speed = 5
self.links = False
self.rechts = False
self.vor = False
self.zurueck = False
self.lenk_winkel = 0
self.block_vor = False
self.block_zurueck = False
def update(self):
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if self.vor or self.zurueck:
self.links = True
if event.key == pygame.K_RIGHT:
if self.vor or self.zurueck:
self.rechts = True
if event.key == pygame.K_UP:
self.vor = True
if event.key == pygame.K_DOWN:
self.zurueck = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
self.links = False
if event.key == pygame.K_RIGHT:
self.rechts = False
if event.key == pygame.K_UP:
self.vor = False
if event.key == pygame.K_DOWN:
self.zurueck = False
self.winkel_berechnung(1)
dx = math.cos(math.radians(self.wagen_winkel))
dy = math.sin(math.radians(self.wagen_winkel))
if self.vor and self.block_vor == False:
self.rect.y -= int(self.speed * dx)
self.rect.x -= int(self.speed * dy)
self.block_zurueck = False
elif self.zurueck and self.block_zurueck == False:
self.rect.y += int(self.speed * dx)
self.rect.x += int(self.speed * dy)
self.block_vor = False
if self.links:
self.lenk_winkel +=5
self.lenk_winkel=min(self.lenk_winkel,120)
elif self.rechts:
self.lenk_winkel -=1
self.lenk_winkel=max(self.lenk_winkel,-120)
if not self.links and not self.rechts: self.lenk_winkel = 0
def winkel_berechnung(self,dt):
if self.rechts:
self.wagen_winkel += self.lenk_winkel
while self.wagen_winkel < 0:
self.wagen_winkel += 360
elif self.links:
self.wagen_winkel += self.lenk_winkel
while self.wagen_winkel > 359:
self.wagen_winkel -= 360
class Lenkung(pygame.sprite.Sprite):
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(lenkrad_img,(120,120)).convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.wagen_winkel = 0
class Landschaft(pygame.sprite.Sprite):
def __init__(self,image):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.mask = pygame.mask.from_surface(self.image )
x=random.randrange(60, breite -60)
y=random.randrange(200, hoehe - 200)
self.rect = self.image.get_rect(center =(x,y))
def zeichne_hintergrund():
background = pygame.image.load("Bilder/background_gelb.jpg")
screen.blit(background,(0,0))
def blitRotateCenter(image, left, top, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (left, top)).center)
screen.blit(rotated_image, new_rect)
return new_rect
########## Bilder laden
auto_img = pygame.image.load("Bilder/car.png")
lenkrad_img = pygame.image.load("bilder/lenkrad.png")
######### Gruppen bilden
auto = MeinAuto(breite/2,hoehe-100)
auto_sprite = pygame.sprite.Group()
auto_sprite.add(auto)
lenkung = Lenkung(breite/2,60)
lenkung_sprite = pygame.sprite.Group()
lenkung_sprite.add(lenkung)
land = pygame.sprite.Group()
while len(land) < anzahl_gegenstaende:
ii = len(land)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,100))
m = Landschaft(img)
if not pygame.sprite.spritecollide(m, land, False):
land.add(m)
while True:
clock.tick(6)
zeichne_hintergrund()
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
hits = pygame.sprite.spritecollide( auto, land, False, pygame.sprite.collide_mask)
for hit in hits:
pass
auto_sprite.update()
land.draw(screen)
new_auto_rect =blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
auto.rect=new_auto_rect
new_auto_rect = new_auto_rect.inflate(-80,-50)
blitRotateCenter(lenkung.image, lenkung.rect.x, lenkung.rect.y, auto.lenk_winkel)
pygame.draw.rect(screen,(0,250,0),auto.rect,4)
pygame.draw.rect(screen,(250,250,0),new_auto_rect,1)
pygame.display.flip()
Instead of the upper left corner of the image, you have to pass the center of the image to the blitRotateCenter function:
new_auto_rect = blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
new_auto_rect = blitRotateCenter(auto.image, auto.rect.centerx, auto.rect.centery, auto.wagen_winkel)
Simplify the code using the asterisk(*) operator:
new_auto_rect = blitRotateCenter(auto.image, *auto.rect.center, auto.wagen_winkel)
This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Simple drag physics, acting differently when moving left or right [duplicate]
(1 answer)
Closed 2 years ago.
Basically I am working on a simple platformer and I have all of the basics like the gravity, acceleration, and platforms, but for some reason the character will decelerate going right, but when it goes left it will continue moving slowly instead of stopping. I even printed the value of my variable "changeX" to see what was going on, and it showed me the values I that should be happening rather than what was displayed. Sorry my comments are very limited in their helpfulness. The code regarding variables rect.x , changeX, and accelX are likely the most relevant.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (50, 50, 255)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
COLOR = BLUE
#Top Speed
tSpeedX = 7
tSpeedY = 20
#gravity constant
gConstant = 1
#acceleration X variable
accelX = 0
#acceleration Y variable
accelY = 0
#whether you can jump or not
jumpB = True
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
#iniatilized values for rect
self.image = pygame.Surface([50, 50])
self.image.fill(COLOR)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.changeX = 0
self.changeY = 0
self.walls = None
#the velocity function. X and Y values are assigned based on the accelX
#and Y constants and added to the points of the rectangle
def velocity(self,x,y):
#change in speed
self.changeX += x
self.changeY += y
if abs(self.changeX) >= tSpeedX:
self.changeX = self.changeX/abs(self.changeX) * tSpeedX
if abs(self.changeY) >= tSpeedY:
self.changeY = self.changeY/abs(self.changeY) * tSpeedY
#standard update function. Will update rectangles position and deaccelerate it if no key held
def jump(self,y):
self.changeY = y
def update(self):
if accelX == 0:
self.changeX *= 0.92
if accelY == 0:
self.changeY *= .95
self.rect.x += self.changeX
print(self.changeX)
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.changeX > 0:
self.rect.right = block.rect.left
else:
self.rect.left = block.rect.right
self.rect.y += self.changeY + (9*gConstant)
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.changeY >= -5:
global jumpB
jumpB = True
COLOR = WHITE
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class wall(pygame.sprite.Sprite):
def __init__(self, sx, sy,px,py):
super().__init__()
#iniatilized values for walls
collision = False
self.image = pygame.Surface([sx, sy])
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = px
self.rect.y = py
pygame.init()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
spriteList = pygame.sprite.Group()
wallList = pygame.sprite.Group()
Player = player(100,100)
spriteList.add(Player)
Wall1 = wall(1000, 30, 0, 400)
Wall2 = wall(100, 30, 150, 350)
wallList.add(Wall2)
spriteList.add(Wall2)
wallList.add(Wall1)
spriteList.add(Wall1)
Player.walls = wallList
clock = pygame.time.Clock()
#Allows program to exit
quit = False
while not quit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit = True
#Sets accel values
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
accelX = -.25
if event.key == pygame.K_RIGHT:
accelX = .25
if event.key == pygame.K_UP and jumpB == True:
Player.jump(-20)
jumpB = False
COLOR = BLUE
if event.key == pygame.K_DOWN:
accelY = .25
#reverses accel values to allow for deaccleration
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
accelX = 0
if event.key == pygame.K_RIGHT:
accelX = 0
if event.key == pygame.K_UP:
accelY = 0
if event.key == pygame.K_DOWN:
accelY = 0
#calls function to move rectangle
Player.velocity(accelX, accelY)
spriteList.update()
screen.fill(BLACK)
spriteList.draw(screen)
pygame.display.flip()
clock.tick(60)
So while working on my engine, I wanted to add an enemy in there, sounded simple enough. Even though my enemy is in the game, not violating the laws of physics (for the most part), the weird part is that I gave him 0 control over movement, but the enemy keeps following the player sprite as I move along.
Now playing around for a bit I've noticed the enemy latches on to the scrolling viewbox while the player is moving, hence the enemy slightly jumps when the player jumps as he hit the down viewbox.
I'm not trying to give him any AI at the moment, the enemy just needs to spawn along with the player, drop to a platform and stand still as the player moves away.
The whole code:
from pygame import *
import time
import pygame
# from colours import *
# from textObjects import small, medium, large
###########################################################################
# COLOURS AND TEXT OBJECTS #
###########################################################################
black = pygame.Color(0, 0, 0)
grey = pygame.Color(128, 128, 128)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
light_blue = pygame.Color(201, 242, 255)
blue = pygame.Color(0, 0, 255)
green_yellow = pygame.Color(212, 255, 0)
yellow = pygame.Color(255, 251, 0)
orange = pygame.Color(255, 166, 0)
orange_red = pygame.Color(255, 85, 0)
pygame.font.init()
small = pygame.font.SysFont(None, 25)
medium = pygame.font.SysFont(None, 50)
large = pygame.font.SysFont(None, 80)
###########################################################################
# CLASSES #
###########################################################################
class Player(pygame.sprite.Sprite):
# Initialise function
def __init__(self, color=blue, width=32, height=48, health=100):
# I assume super() inherits everything from the block class
super(Player, self).__init__()
# Set the image to a Surface of size width and height
self.image = pygame.Surface((width, height))
# Fill the image with the default color of blue
self.image.fill(color)
# Use the Surface of the image to get the rectangular co-ordinates
self.set_properties()
# Create speed for x and y
self.speed_x = 0
self.speed_y = 0
# Create health
self.health = 100
self.level = None
def set_properties(self):
self.rect = self.image.get_rect()
# Create an x and y origin position (Centered the mouse on the sprite)
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
self.speed = 5
# Create total travel distance to check the player's position on the map
self.travel_distance_x = 0
self.travel_distance_y = 0
# Function to easily set the position of any block object on the center
def set_position(self, x, y):
self.rect.x = x - self.origin_x
self.rect.y = y - self.origin_y
# Function made to print the position on the map
def print_position(self):
self.travel_distance_x += self.speed_x
self.travel_distance_y += self.speed_y
# print self.travel_distance_x, self.travel_distance_y
def set_level(self, level):
self.level = level
self.set_position(level.player_start_x, level.player_start_y)
def set_image(self, filename=None):
if filename != None:
self.image = pygame.image.load(filename).convert()
self.set_properties()
def update(self, collidable=pygame.sprite.Group(), event=True):
self.experience_gravity()
self.event = True
self.rect.x += self.speed_x
collision_list = pygame.sprite.spritecollide(self, collidable, False)
for collided_object in collision_list:
# Right direction
if self.speed_x > 0:
self.rect.right = collided_object.rect.left
# Left direction
elif self.speed_x < 0:
self.rect.left = collided_object.rect.right
self.rect.y += self.speed_y
collision_list = pygame.sprite.spritecollide(self, collidable, False)
for collided_object in collision_list:
# Down direction
if self.speed_y > 0:
self.rect.bottom = collided_object.rect.top
self.speed_y = 0
# Up direction
elif self.speed_y < 0:
self.rect.top = collided_object.rect.bottom
self.speed_y = 0
if not event == None:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == pygame.K_a:
self.speed_x = -self.speed
# self.change_speed(-self.speed, 0)
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
self.speed_x = self.speed
# self.change_speed(self.speed, 0)
if event.key == pygame.K_UP or event.key == pygame.K_w:
if len(collision_list) >= 1:
self.speed_y = -(self.speed) * 2
# self.change_speed(0, -self.speed * 2)
if event.key == pygame.K_DOWN:
# self.change_speed(0, self.speed)
pass
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_a:
if self.speed_x < 0:
self.speed_x = 0
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
if self.speed_x > 0:
self.speed_x = 0
def experience_gravity(self, gravity=0.35):
if self.speed_y == 0:
self.speed_y = 1
else:
self.speed_y += gravity
class Enemy(pygame.sprite.Sprite):
# Initialise function
def __init__(self, color=red, width=32, height=48, health=100):
# I assume super() inherits everything from the block class
super(Enemy, self).__init__()
# Set the image to a Surface of size width and height
self.image = pygame.Surface((width, height))
# Fill the image with the default color of blue
self.image.fill(color)
# Use the Surface of the image to get the rectangular co-ordinates
self.set_properties()
# Create speed for x and y
self.speed_x = 0
self.speed_y = 0
# Create health
self.health = 100
self.level = None
def set_properties(self):
self.rect = self.image.get_rect()
# Create an x and y origin position (Centered the mouse on the sprite)
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
self.speed = 5
# Create total travel distance to check the player's position on the map
self.travel_distance_x = 0
self.travel_distance_y = 0
# Function to easily set the position of any block object on the center
def set_position(self, x, y):
self.rect.x = x - self.origin_x
self.rect.y = y - self.origin_y
# Function made to print the position on the map
def print_position(self):
self.travel_distance_x += self.speed_x
self.travel_distance_y += self.speed_y
print self.travel_distance_x, self.travel_distance_y
def set_level(self, level):
self.level = level
self.set_position(level.enemy_start_x, level.enemy_start_y)
def set_image(self, filename=None):
if filename != None:
self.image = pygame.image.load(filename).convert()
self.set_properties()
def update(self, collidable=pygame.sprite.Group(), event=True):
self.experience_gravity()
self.event = True
self.rect.x += self.speed_x
collision_list = pygame.sprite.spritecollide(self, collidable, False)
for collided_object in collision_list:
# Right direction
if self.speed_x > 0:
self.rect.right = collided_object.rect.left
# Left direction
elif self.speed_x < 0:
self.rect.left = collided_object.rect.right
self.rect.y += self.speed_y
collision_list = pygame.sprite.spritecollide(self, collidable, False)
for collided_object in collision_list:
# Down direction
if self.speed_y > 0:
self.rect.bottom = collided_object.rect.top
self.speed_y = 0
# Up direction
elif self.speed_y < 0:
self.rect.top = collided_object.rect.bottom
self.speed_y = 0
if not event == None:
pass
def experience_gravity(self, gravity=0.35):
if self.speed_y == 0:
self.speed_y = 1
else:
self.speed_y += gravity
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color=blue):
# I assume super() inherits everything from the block class
super(Block, self).__init__()
# Set the image to a Surface of size width and height
self.image = pygame.Surface((width, height))
# Fill the image with the default color of blue
self.image.fill(color)
# Get rectangle object of the block
self.rect = self.image.get_rect()
# Assign x and y co-ordinates of the block
self.rect.x = x
self.rect.y = y
def experience_gravity(self, gravity=0.35):
if self.speed_y == 0:
self.speed_y = 1
else:
self.speed_y += gravity
class Level(object):
def __init__(self, player_object):
self.object_list = pygame.sprite.Group()
self.player_object = player_object
self.player_start = self.player_start_x, self.player_start_y = 80, 150
self.enemy_start = self.enemy_start_x, self.enemy_start_y = 300, 200
self.world_shift_x = 0
self.world_shift_y = 0
self.left_viewbox = screen_width / 2 - screen_width / 8
self.right_viewbox = screen_width / 2 + screen_width / 8
self.up_viewbox = screen_height / 3
self.down_viewbox = screen_height / 2 # + screen_height / 12
def update(self):
self.object_list.update()
def draw(self, screen):
screen.fill(white)
self.object_list.draw(screen)
def shift_world(self, shift_x, shift_y):
self.world_shift_x += shift_x
self.world_shift_y += shift_y
for each_object in self.object_list:
each_object.rect.x += shift_x
each_object.rect.y += shift_y
def scroll(self):
if self.player_object.rect.x <= self.left_viewbox:
view_difference = self.left_viewbox - self.player_object.rect.x
self.player_object.rect.x = self.left_viewbox
self.shift_world(view_difference, 0)
if self.player_object.rect.x >= self.right_viewbox:
view_difference = self.right_viewbox - self.player_object.rect.x
self.player_object.rect.x = self.right_viewbox
self.shift_world(view_difference, 0)
if self.player_object.rect.y <= self.up_viewbox:
view_difference = self.up_viewbox - self.player_object.rect.y
self.player_object.rect.y = self.up_viewbox
self.shift_world(0, view_difference)
if self.player_object.rect.y >= self.down_viewbox:
view_difference = self.down_viewbox - self.player_object.rect.y
self.player_object.rect.y = self.down_viewbox
self.shift_world(0, view_difference)
class Level_01(Level):
def __init__(self, player_object):
super(Level_01, self).__init__(player_object)
level = [
#[x, y, width, height, color]
[0, 0, 38, 899, black],
[7, 874, 1592, 25, black],
[1564, 0, 35, 887, black],
[0, 0, 1593, 40, black],
[330, 731, 282, 31, black],
[898, 678, 307, 28, black],
[603, 528, 280, 28, black],
[1279, 616, 301, 32, black],
[1046, 468, 194, 35, black],
[208, 348, 306, 28, black],
[708, 294, 335, 24, black],
[22, 487, 170, 26, black]
]
for block in level:
block = Block(block[0], block[1], block[2], block[3], block[4])
self.object_list.add(block)
class Camera(object):
def __init__(self, camera_function, width, height):
self.camera_function = camera_function
self.state = Rect(0, 0, width, height)
def apply(self, target):
return target.rect.move(self.state.topleft)
def update(self, target):
self.state = self.camera_function(self.state, target.rect)
###########################################################################
# TEXT AND UI FUNCTIONS #
###########################################################################
def set_message(text):
global message, previous_message
message = font.render(text, True, black, white)
previous_message = message
def text_objects(text, color, size):
if size == 'small':
textSurface = small.render(text, True, color)
if size == 'medium':
textSurface = medium.render(text, True, color)
if size == 'large':
textSurface = large.render(text, True, color)
return textSurface, textSurface.get_rect()
def display_message(text, color, y_displacement=0, size='small'):
textSurface, textRectangle = text_objects(text, color, size)
textRectangle.center = (screen_width / 2), (screen_height / 2) + y_displacement
screen.blit(textSurface, textRectangle)
def health_bar(player_health):
if player_health > 85:
health_color = green
elif player_health > 70:
health_color = green_yellow
elif player_health > 55:
health_color = yellow
elif player_health > 40:
health_color = orange
elif player_health > 25:
health_color = orange_red
else:
health_color = red
if player_health < 0:
player_health = 0
pygame.draw.rect(screen, health_color, (50, screen_height / 20, player_health, 25))
###########################################################################
# INITIALISATION, SCREEN PROPERTIES, FPS #
###########################################################################
# Initialise pygame module
pygame.init()
# Initialise pygame font
pygame.font.init()
# Defining the screen size
screen_size = screen_width, screen_height = 800, 600
# Setting the display and getting the Surface object
screen = pygame.display.set_mode(screen_size)
# Getting the Clock object
clock = pygame.time.Clock()
# Setting a title to the window
pygame.display.set_caption("TODO make title")
# Defining variable for FPS
fps_limit = 60
# Clear the screen
screen.fill(white)
# Setting the FPS at which the game will run
clock.tick(fps_limit)
###########################################################################
# MAIN LOOP, PAUSE AND DEATH FUNCTIONS #
###########################################################################
def death():
death = True
while death:
display_message("YOU DIED", red, size='large')
pygame.display.update()
time.sleep(1)
death = False
over = True
game_exit = True
def pause():
paused = True
display_message("Paused", black)
pygame.display.update()
while paused:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
paused = False
elif event.key == pygame.K_q:
pygame.quit()
quit()
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
run=True
intro=False
if event.key == pygame.K_ESCAPE:
pygame.quit()
quit()
screen.fill(light_blue)
display_message("Physics Engine v0.1", black, - screen_height / 5, 'large' )
display_message("pre-alpha stage", grey, - screen_height / 10, 'small')
display_message("Press ENTER to start or ESCAPE to close", black, screen_height / 8, 'small')
pygame.display.update()
def main_loop():
# Group all the currently active objects
active_object_list = pygame.sprite.Group()
# Set variable player to the Player() class
player = Player()
# Set variable enemy to the Enemy() class
enemy = Enemy()
# Add player to the active object list
active_object_list.add(player, enemy)
# Make a list for the levels and append Level_01 to that list with player as the handler (being the character in focus)
level_list = []
level_list.append(Level_01(player))
current_level_number = 0
current_level = level_list[current_level_number]
# Set the starting co-ordinates
player.set_level(current_level)
enemy.set_level(current_level)
# run = True
over = False
game_exit = False
while not game_exit:
if over == True:
while over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
over = False
if event.type == pygame.KEYDOWN:
if event.key == K_RETURN:
main_loop()
if event.key == K_ESCAPE:
pygame.quit()
quit()
screen.fill(light_blue)
display_message("Do you want to start over?", black, -screen_height / 8, size='large')
display_message("Press RETURN to start over or ESCAPE to quit", black, screen_height / 8)
pygame.display.update()
current_events = pygame.event.get()
if current_events:
for event in current_events:
if event.type == pygame.QUIT:
pygame.quit()
quit()
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
pause()
if event.key == pygame.K_k:
player.health -= 5
# Update functions
player.update(current_level.object_list, event)
enemy.update(current_level.object_list, event)
current_level.update()
else:
player.update(current_level.object_list, None)
enemy.update(current_level.object_list, None)
current_level.update()
# Logic testing
current_level.scroll()
if player.health <= 0:
player.health = 0
over = True
death()
if player.travel_distance_y > 900:
player.health = 0
over = True
death()
# Draw everything
current_level.draw(screen)
active_object_list.draw(screen)
health_bar(player.health)
# Delay fps
clock.tick(fps_limit)
# Update screen
pygame.display.update()
game_intro()
main_loop()
The viewbox located in the Level class works in a way that checks if the player is hitting the box, if it is, the world is shifted around the player instead of the player moving in it.
Scroll function
def shift_world(self, shift_x, shift_y):
self.world_shift_x += shift_x
self.world_shift_y += shift_y
for each_object in self.object_list:
each_object.rect.x += shift_x
each_object.rect.y += shift_y
def scroll(self):
if self.player_object.rect.x <= self.left_viewbox:
view_difference = self.left_viewbox - self.player_object.rect.x
self.player_object.rect.x = self.left_viewbox
self.shift_world(view_difference, 0)
if self.player_object.rect.x >= self.right_viewbox:
view_difference = self.right_viewbox - self.player_object.rect.x
self.player_object.rect.x = self.right_viewbox
self.shift_world(view_difference, 0)
if self.player_object.rect.y <= self.up_viewbox:
view_difference = self.up_viewbox - self.player_object.rect.y
self.player_object.rect.y = self.up_viewbox
self.shift_world(0, view_difference)
if self.player_object.rect.y >= self.down_viewbox:
view_difference = self.down_viewbox - self.player_object.rect.y
self.player_object.rect.y = self.down_viewbox
self.shift_world(0, view_difference)
A thing to note is that Level and Level_01 takes "player_object" as an input, which I think is called when player.update() and enemy.update() are called in the main loop.
Main loop
if current_events:
for event in current_events:
if event.type == pygame.QUIT:
pygame.quit()
quit()
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
pause()
if event.key == pygame.K_k:
player.health -= 5
# Update functions
player.update(current_level.object_list, event)
enemy.update(current_level.object_list, event)
current_level.update()
else:
player.update(current_level.object_list, None)
enemy.update(current_level.object_list, None)
current_level.update()
So despite the fact that player is used as a handler for the level in line 477:
level_list.append(Level_01(player))
I think the enemy is influenced by the viewbox because he is also treated as the "player_object" in the scrolling function.
If anyone can give me a few tips on what I'm doing wrong would be very helpful, thanks.
The problem is in your scroll, or rather your shift_world function. You shift every object in self.object_list. For Level01, this list only contains the block objects. In scroll, you then shift every block and the player, but not the enemy. This means the enemy sprite stays at its place and is not shifted with the world as it should be. It therefore appears to move, because its position relative to the world changes. In truth, it stays blitted at the same position on the canvas as it was.
When the player jumps, the enemy ends up in the air once the world has shifted down, and then gravity pulls him back to the platform. When the player moves right, the enemy seems to follow because the world shifts left and the enemy doesn't shift with it.
Add the enemy to your object_list and it should work as expected.
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.