I'm having problems with making my car move. For an assignment, I have to make a simulator. I need to make cars spawn, let them check if the trafficlight's red, orange or green, and the move when the green light appears. How can i make it so that the cars I spawn move along waypoints i specify by using a waypoint-create class I made?
I have the following python file that makes a list of waypoints.
waypoints.py:
class WayPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
class WayPointsList:
def __init__(self):
self.wayPoints = []
def add_wayPoint(self, x, y):
self.wayPoints.append(WayPoint(x, y))
def __len__(self):
return len(self.wayPoints)
def get_wayPoint(self, i):
return [self.wayPoints[i].getX(), self.wayPoints[i].getY()]
When I call this class, the way I use it is like this:
l = WayPointsList
l.add_wayPoint(55,67)
Here's my main.py:
import math
import random, assets
from bus import Bus
from car import Car
from lights import Lights
from waypoints import WayPointsList
import pygame as pg
class Mainstuff(object):
def __init__(self):
pg.init()
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((assets.screen_width, assets.screen_height))
self.background = pg.image.load(assets.background)
self.running = True
self.car_group = pg.sprite.Group()
self.light_group = pg.sprite.Group()
def event_loop(self):
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.KEYDOWN:
if event.key == pg.K_1:
car110 = Car()
car110.set_image(assets.furore)
car110.rotate(180)
car110.set_position(0, 273)
self.car_group.add(car110)
elif event.key == pg.K_2:
car109 = Car()
car109.set_image(assets.hachura)
car109.rotate(180)
car109.set_position(0, 306)
self.car_group.add(car109)
elif event.key == pg.K_3:
car108 = Car()
car108.set_image(assets.jefferson)
car108.rotate(180)
car108.set_position(0, 343)
self.car_group.add(car108)
elif event.key == pg.K_4:
car107 = Car()
car107.set_image(assets.michelli)
car107.rotate(270)
car107.set_position(410, 550)
self.car_group.add(car107)
elif event.key == pg.K_5:
car106 = Car()
car106.set_image(assets.traceAM)
car106.rotate(270)
car106.set_position(460, 550)
self.car_group.add(car106)
elif event.key == pg.K_6:
car105 = Car()
car105.set_image(assets.traceAM)
car105.set_position(750, 300)
self.car_group.add(car105)
elif event.key == pg.K_7:
car104 = Car()
car104.set_image(assets.rumbler)
car104.set_position(750, 265)
self.car_group.add(car104)
elif event.key == pg.K_8:
car103 = Car()
car103.set_image(assets.rumbler)
car103.rotate(90)
car103.set_position(294, 0)
self.car_group.add(car103)
elif event.key == pg.K_9:
car102 = Car()
car102.set_image(assets.rumbler)
car102.rotate(90)
car102.set_position(337, 0)
self.car_group.add(car102)
elif event.key == pg.K_0:
car101 = Car()
car101.set_image(assets.rumbler)
car101.rotate(90)
car101.set_position(380, 0)
self.car_group.add(car101)
elif event.key == pg.K_b:
car201 = Bus()
car201.set_image(assets.bus)
car201.set_position(700, 229)
self.car_group.add(car201)
elif event.key == pg.K_x:
self.car_group.empty()
def update(self):
self.screen.fill(assets.white)
self.car_group.update()
#self.light_group.update()
def draw(self):
self.screen.blit(self.background,(0,0))
self.car_group.draw(self.screen)
self.light_group.draw(self.screen)
def run(self):
while self.running:
self.event_loop()
self.update()
self.draw()
pg.display.update()
self.clock.tick(assets.FPS)
if __name__ == '__main__':
test = Mainstuff()
test.run()
pg.quit()
And here's my car.py:
import pygame, assets, math
class Car(pygame.sprite.Sprite):
def __init__(self):
super(Car, self).__init__()
self.image = pygame.Surface((assets.car_width, assets.car_height))
self.image.fill(assets.red)
self.rect = self.image.get_rect()
self.speed = 1
self.angle = 0
def set_position(self, x, y):
self.rect.x = x
self.rect.y = y
def get_x_position(self):
return self.rect.x
def get_y_position(self):
return self.rect.y
def set_image(self, filename=None):
if filename != None:
self.image = pygame.image.load(filename).convert()
#self.image.fill(assets.green)
self.rect = self.image.get_rect()
def rotate(self, angle):
self.angle = angle
self.image = pygame.transform.rotate(self.image, angle)
self.rect = self.image.get_rect()
#not sure what to do here
def move_position(self, x, y):
# use angle to calculate direction
radius_angle = math.radians(self.angle)
self.rect.x -= self.speed * math.cos(radius_angle)
self.rect.y -= self.speed * math.sin(radius_angle)
print('move', self.angle, self.rect.x, self.rect.y)
def update(self):
self.move_position(0, 0)
I made example.
It takes first point from list as start position and second as target.
It uses pygame.math.Vector2() to calculate one step
current = current + (target - current).normalize() * speed
When player is in target then it get next point from the list as target.
#!/usr/bin/env python3
import pygame
# === CONSTANS === (UPPER_CASE names)
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
# === CLASSES === (CamelCase names)
class Player():
def __init__(self, waypoints, loop=False):
# create green circe
r = 10
self.image = pygame.Surface((2*r, 2*r)).convert_alpha()
self.rect = self.image.get_rect()
self.image.fill((0,0,0,0))
pygame.draw.circle(self.image, (0,255,0), (r, r), r)
# ---
self.loop = loop
self.speed = 5
self.waypoints = waypoints
self.next_point = 0
# set current position
# I use Vector2 because it keeps position as float numbers
# and it makes calcuations easier and more precise
self.current = pygame.math.Vector2(self.waypoints[0])
# set position in rect to draw it
self.rect.center = self.current
# set end point if exists on list
self.target_index = 1
if self.target_index < len(self.waypoints) - 1:
self.target = pygame.math.Vector2(self.waypoints[self.target_index])
self.moving = True
else:
self.target = self.current
self.moving = False
def move(self):
if self.moving:
# get distance to taget
distance = self.current.distance_to(self.target)
#
if distance > self.speed:
self.current = self.current + (self.target - self.current).normalize() * self.speed
self.rect.center = self.current
else:
# put player in tagert place,
# and find new target on list with waypoints
self.current = self.target
self.rect.center = self.current
# set next end point if exists on list
self.target_index += 1
if self.target_index < len(self.waypoints):
self.target = pygame.math.Vector2(self.waypoints[self.target_index])
else:
if self.loop:
self.target_index = 0
else:
self.moving = False
def draw(self, surface):
surface.blit(self.image, self.rect)
# === MAIN === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
# --- objects ---
start = pygame.math.Vector2(screen_rect.centerx, screen_rect.bottom)
end = start
length = 150
waypoints = [(50, 50), (400, 150), (500, 50), (450, 350), (200, 200), (100, 350), (50, 50)]
player = Player(waypoints, True)
# --- mainloop ---
clock = pygame.time.Clock()
is_running = True
while is_running:
# --- events ---
for event in pygame.event.get():
# --- global events ---
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
is_running = False
# --- objects events ---
# empty
# --- updates ---
# empty
player.move()
# --- draws ---
screen.fill(BLACK)
for start, end in zip(waypoints, waypoints[1:]):
pygame.draw.line(screen, RED, start, end)
player.draw(screen)
pygame.display.update()
# --- FPS ---
clock.tick(25)
# --- the end ---
pygame.quit()
BTW: because player doesn't change direction when it moves from one point to another (and target doesn't change position) so it could calculate this value only once
(target - current).normalize() * speed
and recalculate it when it get next point as targe.
BTW: example how to calculate with math module in answer to
finding change in x and y given two points and length of vector
Related
About 500 lines of code, so to make it easier to read I believe the bug comes from _update_asteroids(self) function. Simply put, when the user is in the paused game state and must press play, if the player decides to wait lets say a minute. A minutes worth of asteroids will spawn at the exact same time. I believe this is because the pygame.time.get_ticks() function continues to gain ticks while game is paused. Is there any way to reset the ticks or make it so that 100s of asteroids do not spawn when the user decides to wait before pressing play button?
import pygame
import sys
from pygame.sprite import Sprite
import random
from time import sleep
import pygame.font
class Game(Sprite):
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
super().__init__()
#--------------------------------------------------------------------------------------------
#screen size, color, caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (0,0,255) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.gif') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
self.x = float(self.rect.x)
self.y = float(self.rect.y)
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 0.5 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image, True, False) #holds left image
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#UI
self.health = 100
self.visable = True
self.sb = Scoreboard(self)
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#bullet
self.bullets = pygame.sprite.Group()
self.current_direction = self.direction_right
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#asteroids
self.asteroids = pygame.sprite.Group()
self.next_object_time = 0
self.time_interval = 250
#self._create_asteroids()
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#button
self.game_active = False
self.play_button = Button(self, "Play")
#--------------------------------------------------------------------------------------------
def _create_asteroids(self):
""" create the asteroid shower """
#create a asteroid
number_of_aliens = 1
for asteroid_num in range(number_of_aliens):
self._create_asteroid()
def _create_asteroid(self):
asteroid = Asteroid(self)
if asteroid.x <= 0:
asteroid.direction = 1
elif asteroid.x >= 1160:
asteroid.direction = -1
self.asteroids.add(asteroid)
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.x += self.tank_speed
self.rect.x = self.x
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.x -= self.tank_speed
self.rect.x = self.x
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.tank_speed
self.rect.y = self.y
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.y -= self.tank_speed
self.rect.y = self.y
def draw_healthbar(self):
pygame.draw.rect(self.screen, (255,0,0), (self.rect.x, self.rect.y - 20, 100, 10))
pygame.draw.rect(self.screen, (0,255,0), (self.rect.x, self.rect.y - 20, 100 - (100 - self.health), 10))
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image, self.rect)
def tank_asteroid_collision(self):
#get rid of asteroids that collide with tank
for asteroid in self.asteroids.copy():
if pygame.Rect.colliderect(self.rect, asteroid.rect):
if self.health > 25:
self.health -= 25
self.asteroids.remove(asteroid)
print(self.health)
else:
self._tank_death()
self.sb.reset_score()
self.game_active = False
pygame.mouse.set_visible(True)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
self.bullets.draw(self.screen)
collisions = pygame.sprite.groupcollide(self.bullets, self.asteroids, True, True)
if collisions:
for asteroids in collisions.values():
self.sb.score += 100 * len(asteroids)
self.sb.prep_score()
self.sb.check_high_score()
#draw healthbar if game active
if self.game_active:
self.draw_healthbar()
self.asteroids.draw(self.screen)
#draw the play button and other buttons if game is paused
if not self.game_active:
self.play_button.draw_button()
self.sb.show_score()
pygame.display.flip()
def _tank_death(self):
sleep(0.5)
self.bullets.empty()
self.asteroids.empty()
self.center_ship()
self.health = 100
def _check_KEYDOWN(self, event):
""" when key is press either quit, or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
self.current_direction = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
self.current_direction = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_KEYUP(self, event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
if self.current_direction == self.direction_left:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = False
new_bullet.bullet_shootLeft = True
#change direction of bullet starting point
new_bullet.x -= 100
#add bullet to sprite list
self.bullets.add(new_bullet)
elif self.current_direction == self.direction_right:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = True
new_bullet.bullet_shootLeft = False
#add bullet to sprite list
self.bullets.add(new_bullet)
def center_ship(self):
""" centers the ship in middle of screen """
self.rect.center = self.screen_rect.center
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def _update_tank(self):
""" move tank or quit game based on keypress """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
self.next_object_time += self.time_interval
self._create_asteroids()
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
self._update_tank()
if self.game_active:
self.move()
self.bullets.update()
self.tank_asteroid_collision()
#after certain number of time create a asteroid
self._update_asteroids()
self.asteroids.update()
#get rid of asteroids that have disapeared
for asteroid in self.asteroids.copy():
if asteroid.x >= 1200 or asteroid.x <= 0:
self.asteroids.remove(asteroid)
#get rid of bullets that have disapeared
for bullet in self.bullets.copy():
if bullet.rect.left >= 1200 or bullet.rect.right <= 0:
self.bullets.remove(bullet)
self._update_screen()
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self, game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 2.0
self.bullet_width = 20
self.bullet_height = 5
self.bullet_color = (0, 200, 200)
self.bullet_shootRight = False
self.bullet_shootLeft = False
self.image = pygame.Surface((self.bullet_width, self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
self.rect.midright = game.rect.midright
self.rect.y -= 5 #the tanks barrel is 5 pixels above center
self.rect.x += 15
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
if self.bullet_shootRight:
self.x += self.bullet_speed
self.rect.x = self.x
elif self.bullet_shootLeft:
self.x -= self.bullet_speed
self.rect.x = self.x
class Asteroid(Sprite):
""" a class that represents a single asteroid """
def __init__(self, game):
""" initialize the asteroid and set its starting position """
super().__init__()
self.screen = game.screen
self.speed = 0.1
self.direction = 1
#load the asteroid image onto the screen
self.image = pygame.image.load('images/asteroid.gif')
self.rect = self.image.get_rect()
#start each new asteroid in a random part just outside the map on either the left or right
self.rect.x = random.randint(*random.choice([(0, 0), (1160, 1160)]))
self.rect.y = random.randint(0, 760)
#store the asteroid's exact horizontal positon
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def update(self):
""" move the asteroid to the right or left """
self.x += self.speed * self.direction
self.rect.x = self.x
self.y += (self.speed / 3) * self.direction
self.rect.y = self.y
class Button:
def __init__(self, game, msg):
""" Initialize button """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
#set the dimensions and properties of the button
self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
#Build the button's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
self._prep_msg(msg)
def _prep_msg(self, msg):
""" Turn msg into a rendered image and center text on the button """
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
""" draw blank button then draw message """
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
class Scoreboard:
""" A class to report scoring information """
def __init__(self, game):
""" initialize scorekeeping attributes """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
self.score = 0
self.highscore = 0
# Font settings for scoring information
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_score()
self.prep_highscore()
def reset_score(self):
self.score = 0
self.prep_score()
def prep_highscore(self):
""" Turn the high score into a rendered image. """
high_score = round(self.highscore, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_color, (255, 255, 255))
#center the high score at the top of the screen
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
""" check to see if there's a new high score """
if self.score > self.highscore:
self.highscore = self.score
self.prep_highscore()
def prep_score(self):
""" Turn the score into a renered image """
rounded_score = round(self.score, -1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, (255, 255, 255))
#Display the score at the top of the screen
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right
self.score_rect.top = 0
def show_score(self):
""" draw the score to the screen """
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
if __name__ == '__main__':
a = Game()
a.run_game()
The spawning of the asteroids depends on the next_object_time. next_object_time is initialized with 0. You need to set self.next_object_time when the play button is pressed:
class Game(Sprite):
# [...]
def _check_play_button(self, mouse_pos):
if self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
self.next_object_time = pygame.time.get_ticks() + self.time_interval
To make the algorithm more robust you can set next_object_time depending on the current time when an asteroid spawns (this is optional):
class Game(Sprite):
# [...]
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
# self.next_object_time += self.time_interval
self.next_object_time = current_time + self.time_interval
self._create_asteroids()
I want to know how to Display a "You Win" and a picture at the end of the game when my player reaches 2000 score in the game. I also want to randomly display a hint when the player collides with the class Reseta. Below is my current code. Please be patient with me. Thank you!
import pygame
import os
import random
pygame.init()
pygame.display.set_caption("Chimera")
SCREEN_HEIGHT = 576
SCREEN_WIDTH = 936
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
RUNNING = [pygame.transform.scale(pygame.image.load("images/Main1_side_right.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_1.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_2.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_3.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_4.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_5.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_6.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_7.png"), (64,64))]
JUMPING = pygame.transform.scale(pygame.image.load("images/Main1_jump_right.png"), (64,64))
DUCKING = [pygame.transform.scale(pygame.image.load("images/slide3.png"), (64, 64)),
pygame.transform.scale(pygame.image.load("images/slide3.png"), (64,64))]
TREE = [pygame.transform.scale(pygame.image.load("images/Tree_1.png"), (64,140)),
pygame.transform.scale(pygame.image.load("images/Tree_1.png"), (64,140)),
pygame.transform.scale(pygame.image.load("images/Tree_2.png"), (64,140))]
BOX = [pygame.transform.scale(pygame.image.load("images/box1.png"), (110,90)),
pygame.transform.scale(pygame.image.load("images/box2.png"), (110,90)),
pygame.transform.scale(pygame.image.load("images/box3.png"), (110,90))]
SHADOW = [pygame.transform.scale(pygame.image.load("images/Enemy_1.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Enemy_2.png"), (64,64)),]
PORTAL = [pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128)),
pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128)),
pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128))]
RESETA = [pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120)),
pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120)),
pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120))]
DRUG = [pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90)),
pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90)),
pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90))]
STANDING = pygame.transform.scale(pygame.image.load("images/Main1_front.png"), (64,64))
BG = pygame.image.load(os.path.join("images", "Background_2.jpg"))
class Boy:
X_POS = 80
Y_POS = 390
Y_POS_DUCK = 430
JUMP_VEL = 8.5
def __init__(self):
self.duck_img = DUCKING
self.run_img = RUNNING
self.jump_img = JUMPING
self.boy_duck = False
self.boy_run = True
self.boy_jump = False
self.step_index = 0
self.jump_vel = self.JUMP_VEL
self.image = self.run_img[0]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS
def update(self, userInput):
if self.boy_duck:
self.duck()
if self.boy_run:
self.run()
if self.boy_jump:
self.jump()
if self.step_index >= 10:
self.step_index = 0
if userInput[pygame.K_UP] and not self.boy_jump:
self.boy_duck = False
self.boy_run = False
self.boy_jump = True
elif userInput[pygame.K_DOWN] and not self.boy_jump:
self.boy_duck = True
self.boy_run = False
self.boy_jump = False
elif not (self.boy_jump or userInput[pygame.K_DOWN]):
self.boy_duck = False
self.boy_run = True
self.boy_jump = False
def duck(self):
self.image = self.duck_img[self.step_index // 5]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS_DUCK
self.step_index += 1
def run(self):
self.image = self.run_img[self.step_index // 5]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS
self.step_index += 1
def jump(self):
self.image = self.jump_img
if self.boy_jump:
self.boy_rect.y -= self.jump_vel * 4
self.jump_vel -= 0.8
if self.jump_vel < - self.JUMP_VEL:
self.boy_jump = False
self.jump_vel = self.JUMP_VEL
def draw(self, SCREEN):
SCREEN.blit(self.image, (self.boy_rect.x, self.boy_rect.y))
class Obstacle:
def __init__(self, image, type):
self.image = image
self.type = type
self.rect = self.image[self.type].get_rect()
self.rect.x = SCREEN_WIDTH
def update(self):
self.rect.x -= game_speed
if self.rect.x < -self.rect.width:
obstacles.pop()
def draw(self, SCREEN):
SCREEN.blit(self.image[self.type], self.rect)
class Box(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 380
class Tree(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 325
class Shadow(Obstacle):
def __init__(self, image):
self.type = 0
super().__init__(image, self.type)
self.rect.y = 390
self.index = 0
def draw(self, SCREEN):
if self.index >= 9:
self.index = 0
SCREEN.blit(self.image[self.index//5], self.rect)
self.index += 1
class Drug(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 325
class Portal(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 300
class Reseta(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 350
def main():
global game_speed, x_pos_bg, y_pos_bg, points, obstacles
run = True
clock = pygame.time.Clock()
player = Boy()
# cloud = Cloud()
game_speed = 10
x_pos_bg = 0
y_pos_bg = 0
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
obstacles = []
death_count = 0
def score():
global points, game_speed
points += 1
if points % 500 == 0:
game_speed += 1
text = font.render("Points: " + str(points), True, (0, 0, 0))
textRect = text.get_rect()
textRect.center = (850, 30)
SCREEN.blit(text, textRect)
def background():
global x_pos_bg, y_pos_bg
image_width = BG.get_width()
SCREEN.blit(BG, (x_pos_bg, y_pos_bg))
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
if x_pos_bg <= -image_width:
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
x_pos_bg = 0
x_pos_bg -= game_speed
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
SCREEN.fill((255, 255, 255))
userInput = pygame.key.get_pressed()
background()
player.draw(SCREEN)
player.update(userInput)
if len(obstacles) == 0:
if random.randint(0, 2) == 0:
obstacles.append(Box(BOX))
elif random.randint(0, 2) == 1:
obstacles.append(Tree(TREE))
elif random.randint(0, 2) == 2:
obstacles.append(Shadow(SHADOW))
elif random.randint(0, 2) == 0:
obstacles.append(Portal(PORTAL))
elif random.randint(0, 2) == 0:
obstacles.append(Reseta(RESETA))
elif random.randint(0, 2) == 0:
obstacles.append(Drug(DRUG))
for obstacle in obstacles:
obstacle.draw(SCREEN)
obstacle.update()
if player.boy_rect.colliderect(obstacle.rect):
pygame.time.delay(2000)
death_count += 1
menu(death_count)
score()
clock.tick(30)
pygame.display.update()
def menu(death_count):
global points
run = True
while run:
# SCREEN.fill((255, 255, 255))
SCREEN.blit(BG, (0,0))
font = pygame.font.Font('freesansbold.ttf', 30)
if death_count == 0:
text = font.render("Press any Key to Start", True, (250, 245, 225))
save = font.render("Score 1000 to save the Girl", True, (250, 245, 225))
saveRect = save.get_rect()
saveRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)
SCREEN.blit(save, saveRect)
elif death_count > 0:
text = font.render("Press any Key to Restart", True, (250, 245, 225))
score = font.render("Your Score: " + str(points), True, (250, 245, 225))
scoreRect = score.get_rect()
scoreRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)
SCREEN.blit(score, scoreRect)
textRect = text.get_rect()
textRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
SCREEN.blit(text, textRect)
SCREEN.blit(STANDING, (SCREEN_WIDTH // 2 - 20, SCREEN_HEIGHT // 2 - 140))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
if event.type == pygame.KEYDOWN:
main()
menu(death_count=0)
below is the file for the folder of images used in my game.
https://drive.google.com/file/d/1t_kDNw3G1Q6X4KKZ9IfsCmYKpEecz9Ci/view?usp=sharing
There is a very simple way that you can have a "You Win" show up in your game when the score reaches 200. You would need a variable for the score that increases every time something happens. First, you would have to load an image for the "You Win" by doing pygame.image.load(file name.png) then you would have to assign variables for the x-axis and y-axis of the image (I used You_WinX and You_WinY for the example) then you can simply do this:
if score == 2000:
You_WinX = (location on x-axis)
You_WinY = (location in y-axis)
What this would do is when the score reaches the value of 2000, the image of "You Win" will appear where you want it to be on the screen (x,y)
I created a little exemple for you. The only thing you have to remember is the purpose of the method main_loop() contained in my class Game (comments will help you to understand it). Just remember that you add several while loops that depends on a variable you will change in order to change state of the game. Each loop print out different things according to what is its goal.
For the point to reach, just create an invisible rect and check if your player rect collide with the invisible one. If it does, switch your variables in order to get into another loop that will print out something else than your game (in this case you could print out "You won".
For the score, you could add score every time the player does something and check every frame if the score exceed the score you have set to win. In this case, just switch variables in order to change state of the game.
import pygame
# creating screen
screen = pygame.display.set_mode((1000, 500))
class Player(pygame.sprite.Sprite):
"""Defining a little class just to show the player on the screen"""
def __init__(self):
# initialize super class (pygame.sprite.Sprite)
super().__init__()
# defining rect of the player
self.rect = pygame.Rect((100, 100), (100, 100))
self.color = (255, 0, 0)
def move_right(self):
self.rect.x += 20
def show_player(self):
pygame.draw.rect(screen, self.color, self.rect)
class Game:
"""Defining the game class that contains the main loop and main variables"""
def __init__(self):
# defining variables for game loops
self.running = True
self.end_menu = False
self.game = True
# initialising player class
self.pl = Player()
# creating an invisible rect that the player has to reach
self.reach_point = pygame.Rect((500, 100), (100, 100))
def main_loop(self):
"""The goal here is to create several loops. One will be for the main game loop that will contains
the two other loops (as many as you want), when an event you chose will happens, the variable that makes
the loop run will turn on false and the variable that makes the loop of the other stage you want to reach
will turn on true. So you will constantly run a loop because your main loop will end up only if you quit the
game."""
while self.running:
while self.game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
self.game = False
if event.type == pygame.KEYDOWN:
# defining just a key for the example
if event.key == pygame.K_d:
self.pl.move_right()
# detecting if the player reach the invisible rect
if self.pl.rect.colliderect(self.reach_point):
self.game = False
self.end_menu = True
# fill the screen in white
screen.fill((255, 255, 255))
# shows the player
self.pl.show_player()
# update the screen
pygame.display.flip()
while self.end_menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
self.end_menu = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
self.game = True
self.end_menu = False
# fills the screen in order to see the different states of the game
screen.fill((255, 255, 0))
# update the screen
pygame.display.flip()
# initializing the class game
game = Game()
pygame.init()
# calling function `main_loop()` contained in the class Game (initialised as game)
game.main_loop()
pygame.quit()
I use pygame and python. I want to make a simple game. Two tanks in scene shoot each other. I have tank class:
class playerObject(pygame.sprite.Sprite):
def __init__(self, rect):
super().__init__()
self.__original_image = pygame.image.load(config.PATH_TO_IMAGES + '\\tank.png').convert_alpha()
self.__original_image = pygame.transform.scale(self.__original_image, (rect.width, rect.height))
self.image = self.__original_image
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
self.lookAtVector = Vector2(-3, 0)
def addBall(self):
if len(self.balls) < 5:
self.balls.append(ballObject(pygame.Rect(self.rect.centerx, self.rect.top, 10, 10)))
self.balls[-1].lookAtVector.x = self.lookAtVector.x
self.balls[-1].lookAtVector.y = self.lookAtVector.y
angle = 0
__original_image = 0
image = 0
lookAtVector = 0
balls = []
And bullet class:
class ballObject(pygame.sprite.Sprite):
def __init__(self, rect):
super().__init__()
self.image = pygame.image.load(config.PATH_TO_IMAGES + '\\tank.png').convert_alpha()
self.image = pygame.transform.scale(self.image, (5, 5))
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
self.lookAtVector = Vector2(-3, 0)
lookAtVector = 0
currentSpeed = 1.5
But ball spawns on center of tank. How to do that ball will spawn on tank gun?
tank.png
bullet.png
It needs basic trigonometry.
At start keep distance from center of tank to end of its barrel
self.distance = self.rect.height//2
and later get position of barrel base on angle
def get_barrel_end(self):
rad = math.radians(self.angle)
x = self.rect.centerx - math.sin(rad) * self.distance
y = self.rect.centery - math.cos(rad) * self.distance
return x, y
So you have position for bullet.
I use module math but maybe you could do it with pygame.math
EDIT: The same with pygame.math.Vector2()
At start keep distance from center of tank to end of its barrel
self.distance_vector = pygame.math.Vector2(0, -self.rect.height//2)
and later get position of barrel base on angle
def get_barrel_end(self):
return self.rect.center + self.distance_vector.rotate(-self.angle)
Working example - it draws tank in center and bullet on the end of tank's barrel. Using left/right arrow you can rotate tank and bullet keeps its position on end of tank's barrel.
import pygame
import math
# --- constants ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 15
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# --- classes ---
class Tank():
def __init__(self, x, y):
self.image_original = pygame.image.load('tank.png').convert_alpha()
self.angle = 0
self.dirty = False
self.image = self.image_original.copy()
self.rect = self.image.get_rect(center=(x, y))
self.turn_left = False
self.turn_right = False
#self.distance = self.rect.height//2
self.distance_vector = pygame.math.Vector2(0, -self.rect.height//2)
def draw(self, screen):
screen.blit(self.image, self.rect)
def update(self):
if self.turn_left:
self.angle = (self.angle + 2) % 360
self.dirty = True
if self.turn_right:
self.angle = (self.angle - 2) % 360
self.dirty = True
if self.dirty:
self.dirty = False
#print(self.angle)
self.image = pygame.transform.rotate(self.image_original, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def handle_event(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.turn_left = True
elif event.key == pygame.K_RIGHT:
self.turn_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
self.turn_left = False
elif event.key == pygame.K_RIGHT:
self.turn_right = False
#def get_barrel_end(self):
# rad = math.radians(self.angle)
# x = self.rect.centerx - math.sin(rad) * self.distance
# y = self.rect.centery - math.cos(rad) * self.distance
# return x, y
def get_barrel_end(self):
return self.rect.center + self.distance_vector.rotate(-self.angle)
class Bullet():
def __init__(self, x, y):
self.image = pygame.image.load('bullet.png').convert_alpha()
self.rect = self.image.get_rect(center=(x, y))
self.dirty = False
def draw(self, screen):
screen.blit(self.image, self.rect)
def update(self, x, y):
self.rect.center = (x, y)
def handle_event(self, event):
pass
# --- main ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))#, 32)
screen_rect = screen.get_rect()
tank = Tank(*screen_rect.center)
bullet = Bullet(*tank.get_barrel_end())
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
tank.handle_event(event)
# - updates -
tank.update()
bullet.update(*tank.get_barrel_end())
# - draws -
screen.fill(WHITE)
tank.draw(screen)
bullet.draw(screen)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
First post here. So I am trying to implement a Civilization type of movement game. At the moment, I have one sprite in a cell. I can click it and then if I click another grid, the sprite moves there. What I now want is to spawn 5-6 such sprites, and then do the same thing. Click on a sprite and then click another grid, and that specific sprite moves there without affecting the other sprites. I cannot seem to do that. I can spawn 5-6 random sprites at different grids, but when I click on one of them and then click another grid, all the other sprites are gone. The code is below (not the best as I am learning Pygame). I understand that I have to somehow only update the sprite that was clicked, but I am not sure how to do that.
import pygame
import random
WIDTH = 900
HEIGHT = 700
FPS = 2
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
def main():
# Pygame sprite Example
global x_lines
global y_lines
x_lines = [WIDTH-i*WIDTH/20 for i in range(20,0, -1)]
y_lines = [HEIGHT-j*HEIGHT/20 for j in range(20,0, -1)]
class TeamOne(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
# this line is required to properly create the sprite
pygame.sprite.Sprite.__init__(self)
# create a plain rectangle for the sprite image
self.image = pygame.Surface((WIDTH / 20, HEIGHT / 20))
self.image.fill(GREEN)
# find the rectangle that encloses the image
self.rect = self.image.get_rect()
# center the sprite on the screen
self.rect.center = ((random.randint(1,19)*2+1)* WIDTH/ 40, (random.randint(1,19)*2+1)*HEIGHT/40)
def update(self, position):
# any code here will happen every time the game loop updates
(a, b) = position
for index, i in enumerate(x_lines):
if i > a:
self.rect.x = x_lines[index-1]
break
for index, j in enumerate(y_lines):
if j > b:
self.rect.y = y_lines[index-1]
break
# initialize pygame and create window
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
clicked_sprites = pygame.sprite.Group()
teamone_sprites = pygame.sprite.Group()
for i in range(5):
mob1 = TeamOne()
teamone_sprites.add(mob1)
# Game loop
running = True
j=0
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN and j == 0:
pos = pygame.mouse.get_pos()
for s in teamone_sprites:
if s.rect.collidepoint(pos):
#teamone_sprites.add(s)
clicked_sprites.add(s)
print (clicked_sprites)
j = 1
elif event.type == pygame.MOUSEBUTTONDOWN and j == 1:
new_pos = pygame.mouse.get_pos()
#teamone_sprites.update(new_pos)
clicked_sprites.update(new_pos)
j = 0
# Update
# Draw / render
## screen.fill(BLACK)
## draw_grid(screen)
##
## teamone_sprites.draw(screen)
##
##
##
## # *after* drawing everything, flip the display
## pygame.display.flip()
# Draw / render
screen.fill(BLACK)
draw_grid(screen)
teamone_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
def draw_grid(screen):
for i in range(1, HEIGHT, int(HEIGHT/20)):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, int(WIDTH/20)):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
if __name__ == '__main__':
main()
Some tips for you:
Keep your main loop clean
Put logic where it belongs
Only call pygame.display.flip()/pygame.display.update() once
Don't use variable names like j
Since your game is grid based, you should have a way to translate between grid coordinates and screen coordinates
Here's a simple runnable example I hacked together (see the comments for some explanations):
import pygame
import random
WIDTH = 900
HEIGHT = 700
ROWS = 20
COLUMNS = 20
TILE_SIZE = WIDTH / COLUMNS, HEIGHT / ROWS
TILE_W, TILE_H = TILE_SIZE
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
# some functions to translate grid <-> screen coordinates
def posToScreen(pos):
column, row = pos
return column * TILE_W, row * TILE_H
def screenToPos(pos):
column, row = pos
return column / TILE_W, row / TILE_H
def draw_grid(screen):
for i in range(1, HEIGHT, TILE_H):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, TILE_W):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
# a class that handles selecting units
class Cursor(pygame.sprite.Sprite):
def __init__(self, units, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
# group of the units that can be controlled
self.units = units
# we create two images
# to indicate if we are selecting or moving
self.image = pygame.Surface(TILE_SIZE)
self.image.set_colorkey((43,43,43))
self.image.fill((43,43,43))
self.rect = self.image.get_rect()
self.selected_image = self.image.copy()
pygame.draw.rect(self.image, pygame.Color('red'), self.image.get_rect(), 4)
pygame.draw.rect(self.selected_image, pygame.Color('purple'), self.image.get_rect(), 4)
self.base_image = self.image
self.selected = None
def update(self):
# let's draw the rect on the grid, based on the mouse position
pos = pygame.mouse.get_pos()
self.rect.topleft = posToScreen(screenToPos(pos))
def handle_click(self, pos):
if not self.selected:
# if we have not selected a unit, do it now
for s in pygame.sprite.spritecollide(self, self.units, False):
self.selected = s
self.image = self.selected_image
else:
# if we have a unit selected, just set its target attribute, so it will move on its own
self.selected.target = posToScreen(screenToPos(pos))
self.image = self.base_image
self.selected = None
class TeamOne(pygame.sprite.Sprite):
def __init__(self, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
self.image = pygame.Surface(TILE_SIZE)
self.image.fill(GREEN)
self.pos = random.randint(0, COLUMNS), random.randint(0, ROWS)
self.rect = self.image.get_rect(topleft = posToScreen(self.pos))
self.target = None
def update(self):
# do nothing until target is set
# (maybe unset it if we reached our target)
if self.target:
if self.rect.x < self.target[0]:
self.rect.move_ip(1, 0)
elif self.rect.x > self.target[0]:
self.rect.move_ip(-1, 0)
elif self.rect.y < self.target[1]:
self.rect.move_ip(0, 1)
elif self.rect.y > self.target[1]:
self.rect.move_ip(0, -1)
self.pos = screenToPos(self.rect.topleft)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.LayeredUpdates()
team_ones = pygame.sprite.Group()
for i in range(5):
TeamOne(all_sprites, team_ones)
cursor = Cursor(team_ones, all_sprites)
# a nice, simple, clean main loop
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# we could also pass all events to all sprites
# so we would not need this special clause for the cursor...
if event.type == pygame.MOUSEBUTTONDOWN:
cursor.handle_click(event.pos)
all_sprites.update()
screen.fill(BLACK)
draw_grid(screen)
all_sprites.draw(screen)
pygame.display.flip()
if __name__ == '__main__':
main()
I have pretty much the same Problem i have a healthbar for my enemie but i want all enemys to have one so its a spritegroup now if i want to change an attribute of one object out of my spritegroup i dont know how to properly access it. The Problem lays in the Healthbaranimation function. I tried self.healthbar.sprites() self.healthbar.sprites and spritedict nothing really semms to work. Is there an easy way to fix this? P.s sorry for my bad code It is my first real attempt making a small game
from os import path
import pygame
from elements.ammo import AMMO
from elements.bigenemy import BIGENEMY
from elements.enemy import ENEMY
from elements.player import PLAYER
from .base import BaseState
from elements.healthbar import HEALTHBAR
class Gameplay(BaseState):
def __init__(self):
super(Gameplay, self).__init__()
self.next_state = "GAME_OVER"
self.x, self.y = 100, 1030
self.playersprite = PLAYER((self.x, self.y))
self.bigenemy = pygame.sprite.GroupSingle(BIGENEMY())
self.bottomrect = pygame.Rect((0, 1030), (1920, 50))
self.enemysprite = ENEMY()
self.ammosprite = AMMO()
self.healthbar = pygame.sprite.Group(HEALTHBAR())
self.displayedimage = self.playersprite.image
self.displayedrect = self.playersprite.rect
self.highscore = self.load_data()
self.points = 0
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "red")
self.nhstext = "NEW HIGHSCORE!"
self.nhssurf = self.font.render(self.nhstext, True, "red")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")
self.bulletgroup = pygame.sprite.Group()
self.time_active = 0
self.bigenemyexisting = True
def get_event(self, event):
if event.type == pygame.QUIT:
self.quit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LCTRL:
self.playersprite.crouching = True
elif event.key == pygame.K_SPACE:
self.playersprite.jumping = True
elif event.key == pygame.K_q and self.playersprite.ammunition != 0:
self.playersprite.shooting = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
self.done = True
elif event.key == pygame.K_LCTRL:
self.playersprite.crouching = False
elif event.key == pygame.K_q:
self.playersprite.shooting = False
def draw(self, surface):
surface.fill(pygame.Color("black"))
pygame.draw.rect(surface, "red", self.bottomrect)
surface.blit(self.displayedimage, (self.displayedrect))
surface.blit(self.enemysprite.image, (self.enemysprite.rect))
surface.blit(self.ammosprite.image, (self.ammosprite.rect))
self.healthbar.draw(surface)
self.bigenemy.draw(surface)
self.bulletgroup.draw(surface)
surface.blit(self.scoresurf, (0, 0))
surface.blit(self.ammosurf, (0, 1000))
if self.points > self.highscore: surface.blit(self.nhssurf, (1920 / 2 - 100, 1080 / 2))
def lost(self):
self.enemysprite.startposx = 1920
self.enemysprite.startposy = self.enemysprite.gettypeenemy()
self.enemysprite.speed = 10
self.highscorefunc()
self.points = 0
self.playersprite.ammunition = 30
def collidecheck(self):
self.playermask = pygame.mask.from_surface(self.displayedimage)
self.enemymask = pygame.mask.from_surface(self.enemysprite.image)
offsetx = self.enemysprite.rect.left - self.displayedrect.left
offsety = self.enemysprite.rect.top - self.displayedrect.top
if self.displayedrect.colliderect(self.enemysprite.rect):
if self.playermask.overlap(self.enemymask, (offsetx, offsety)):
self.lost()
self.done = True
elif self.enemysprite.rect.x < 0 and self.enemysprite.speed < 25:
self.points += 1
self.enemysprite.speed += 1
elif self.enemysprite.speed > 25:
self.enemysprite.speed += .5
elif self.displayedrect.colliderect(self.ammosprite.rect):
self.ammosprite.startposx = 2300
self.playersprite.ammunition += 30
elif pygame.sprite.groupcollide(self.bigenemy,self.bulletgroup,False,True):
self.bigenemy.sprite.health -= 10
def shooting(self, dt):
if self.playersprite.ammunition != 0:
if self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
self.time_active += dt
if self.time_active >= 100:
self.bulletgroup.add(self.playersprite.createbullet())
self.time_active = 0
self.playersprite.ammunition -= 1
else:
self.playersprite.shooting = False
def highscorefunc(self):
if self.points > self.highscore:
self.highscore = self.points
with open(path.join(self.dir, self.HS_FILE), 'w') as f:
f.write(str(self.highscore))
def animation(self):
if not self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
if self.playersprite.index >= len(self.playersprite.basicanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.basicanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.shooting and not self.playersprite.jumping:
if self.playersprite.index >= len(self.playersprite.shootanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.shootanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.jumping:
self.displayedimage = self.playersprite.imagejump
elif self.playersprite.crouching:
self.displayedimage = self.playersprite.slidingimage
def healthbaranimation(self):
if self.bigenemy.sprite.health < 90:
self.healthbar.spritedict.index = 1
if self.bigenemy.sprite.health < 80:
self.healthbar.sprite.index = 2
if self.bigenemy.sprite.health < 70:
self.healthbar.sprite.index = 3
if self.bigenemy.sprite.health < 60:
self.healthbar.sprite.index = 4
if self.bigenemy.sprite.health < 50:
self.healthbar.sprite.index = 5
if self.bigenemy.sprite.health < 40:
self.healthbar.sprite.index = 6
if self.bigenemy.sprite.health < 30:
self.healthbar.sprite.index = 7
if self.bigenemy.sprite.health < 20:
self.healthbar.sprite.index = 8
if self.bigenemy.sprite.health < 10:
self.healthbar.sprite.index = 9
def spawnbigenemies(self):
if self.bigenemyexisting:
if self.bigenemy.sprite.health < 3:
self.bigenemy.add(BIGENEMY())
self.bigenemyexisting = True
def update(self, dt):
try:
self.bigenemy.sprite.update()
except:
pass
self.healthbaranimation()
self.healthbar.update()
self.playersprite.jump()
self.animation()
self.shooting(dt)
self.bulletgroup.update()
self.enemysprite.update()
self.ammosprite.update()
self.collidecheck()
self.spawnbigenemies()
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "black")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")
I am building a game and I keep on running up against this error. I can't seem to fix it. I believe the problem is either in the function "main" at the bottom or in the classes "Level" and "Level01". If you find there is a way I can improve my code can you also tell me as I am just learning how to build games with OOP.
File "C:/Users/fabma/Documents/PythonGames/RPG/Scroller!.py", line 148, in main
currentLevel.drawer(display)
TypeError: drawer() missing 1 required positional argument: 'display1'
Here is my code:
import pygame
# Colours + Global constants
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WIDTH = 800
HEIGHT = 600
SIZE = (WIDTH, HEIGHT)
# CLASSES
# Block is the common platform
class Block(pygame.sprite.Sprite):
def __init__(self, length, height, colour):
super().__init__()
# Making image
self.image = pygame.Surface([length, height])
self.image.fill(colour)
self.rect = self.image.get_rect()
# Setting Y coordinates
self.rect.y = HEIGHT * 0.95
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
# Is it touching the floor?
self.velocity = 0
self.standing = True
# Rendering image and creating some variables
self.height = 40
self.length = 40
self.sprite_x_change = 0
self.sprite_y_change = 0
self.image = pygame.Surface([self.height, self.length])
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.y = HEIGHT * 0.884
self.level = None
# Mobility: Left, right, up and stop
def move_right(self):
self.sprite_x_change = 15
def move_left(self):
self.sprite_x_change = -15
def move_up(self, platform):
# Seeing if we hit anything if so then we can jump!
self.rect.y -= 2
hit_list = pygame.sprite.spritecollide(self, platform, False)
if len(hit_list) > 0 or self.rect.bottom >= HEIGHT - Block.height:
self.change_y = -10
def stop(self):
self.sprite_x_change = 0
def updater(self):
self.gravity()
platforms_hit = pygame.sprite.spritecollide(self, self.level.platforms, False)
for blocks in platforms_hit:
self.sprite_y_change = 0
# Going down
if self.sprite_y_change > 0:
self.rect.bottom = blocks.rect.top
self.velocity = 0
self.standing = True
# Going up
if self.sprite_y_change < 0:
self.rect.top = blocks.rect.bottom
self.standing = False
if self.sprite_x_change > 0:
self.rect.right = blocks.rect.left
if self.sprite_x_change < 0:
self.rect.left = blocks.rect.right
if self.sprite_x_change == 0 and self.sprite_y_change == 0:
self.rect.y = HEIGHT * 0.884
if self.standing == False:
self.velocity += 1
self.rect.x += self.sprite_x_change
self.rect.y += self.sprite_y_change
def gravity(self):
self.sprite_y_change += 0.980665*self.velocity
class Level:
def __init__(self):
# Creating groups
self.sprites = pygame.sprite.Group()
self.all_things = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
def drawer(self, display1):
display1.fill(BLUE)
self.all_things.draw(display1)
class Level01(Level):
def __init__(self, player1):
# Initialise level1
Level.__init__(self)
# Level01 things
block = Block(WIDTH, HEIGHT * 0.05, RED)
Level.all_things = self.all_things
self.sprites.add(player1)
self.platforms.add(block)
self.all_things.add(player1, block)
def main():
# Init pygame
pygame.init()
# Set screen
display = pygame.display.set_mode(SIZE)
# Creating FPS thingy
clock = pygame.time.Clock()
# Making levels + Player
player = Player()
level_1 = Level01(player)
# Choosing level
levelList = []
levelList.append(Level01)
currentLevelNumber = 0
currentLevel = levelList[currentLevelNumber]
# Game loop
loop = True
while loop == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.move_right()
if event.key == pygame.K_LEFT:
player.move_left()
if event.key == pygame.K_UP:
player.move_up(currentLevel.platforms)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and player.sprite_x_change < 0:
player.stop()
if event.key == pygame.K_RIGHT and player.sprite_x_change > 0:
player.stop()
# Update things
currentLevel.all_things.update()
currentLevel.drawer(display)
# Refresh screen
clock.tick(30)
pygame.display.update()
pygame.quit()
if __name__ == "__main__":
main()
You need to create an instance of your level rather than just appending the class itself into your list:
levelList.append(Level01)
should be...
levelList.append(level_1)
As it stands, you're using the class object rather than an instance of it, which means that the display you're passing is getting put into the self argument (because the class object won't pass along an instance, because it's not one). Once you're calling it on an instance of the class, the self argument will be automatically passed and thus your display argument will get passed to the correct spot.