0

I am currently working on a game on Pygame in Python. It is a player vs player battle game. When the program is run the value error subsurface rectangle outside surface area is shown. I searched about the error but could not understand about it as I am new to Pygame.

My program-

import pygame
from Players import Characters

pygame.init()

#Game window
swidth = 1000
sheight= 600
s=pygame.display.set_mode((swidth, sheight))
pygame.display.set_caption("Brawler")

#Framerate
clock = pygame.time.Clock()
FPS = 60

#Game variables
count=3
last_count_update=pygame.time.get_ticks()
score = [0, 0]#player scores. [P1, P2]
round_over = False
round_cooldown = 2000

#Player variables
Knight_size = 278
Knight_scale = 4
Knight_offset = [72, 56]
Knight_data = [Knight_size, Knight_scale, Knight_offset]

Assassin_size = 288
Assassin_scale = 3
Assassin_offset = [112, 107]
Assassin_data =[ Assassin_size, Assassin_scale, Assassin_offset]

#load background image
bg_image = pygame.image.load("G1/Assets/Bg.png").convert_alpha()

#load spritesheets
Knight_sheet = pygame.image.load("G1/Assets/Knight/Sprites.png").convert_alpha()
Assassin_sheet = pygame.image.load("G1/Assets/Assassin/Sprites.png").convert_alpha()

#load vicory image
victory_img = pygame.image.load("G1/Assets/victory.png").convert_alpha()

#define number of steps in each animation
Knight_steps=[8, 8, 20, 8, 11, 19, 10, 6, 13]
Assassin_steps=[8, 8, 6, 6, 8, 18, 7, 6, 19]

#Font
count_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 80)
score_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 30)

#Text
def draw_text(text, font, text_col, x, y):
  img = font.render(text, True, text_col)
  s.blit(img, (x, y))

#Background
def draw_bg():
  scaled_bg = pygame.transform.scale(bg_image, (swidth, sheight))
  s.blit(scaled_bg, (0, 0))

#Health bars
red=(255, 0, 0)
yellow=(255, 255, 0)
white=(255, 255, 255)
def draw_health_bar(health, x, y):
  ratio = health / 100
  pygame.draw.rect(s, white, (x - 2, y - 2, 404, 34))
  pygame.draw.rect(s, red, (x, y, 400, 30))
  pygame.draw.rect(s, yellow, (x, y, 400 * ratio, 30))

#Player avatars
Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
Assassin=Characters(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)

#game loop
run = True
while run:

  clock.tick(FPS)

  #Display background
  draw_bg()

  #Display health bar
  draw_health_bar(Knight.health, 20, 20)
  draw_health_bar(Assassin.health, 580, 20)
  draw_text("P1: " + str(score[0]), score_font, red, 20, 60)
  draw_text("P2: " + str(score[1]), score_font, red, 580, 60)

  #countdown
  if intro_count <= 0:
    #Move fighters
    Knight.move(swidth, sheight, s, Assassin, round_over)
    Assassin.move(swidth, sheight, s, Knight, round_over)
  else:

    #Display count timer
    draw_text(str(intro_count), count_font, red, swidth/2, sheight/3)

    #update count timer
    if (pygame.time.get_ticks()-last_count_update)>= 1000:
      intro_count -= 1
      last_count_update = pygame.time.get_ticks()

  #Update fighters
  Knight.update()
  Assassin.update()

  #Display fighters
  Knight.draw(s)
  Assassin.draw(s)

  #check for player defeat
  if round_over == False:
    if Knight.alive == False:
      score[1] += 1
      round_over = True
      round_over_time = pygame.time.get_ticks()
    elif Assassin.alive == False:
      score[0] += 1
      round_over = True
      round_over_time = pygame.time.get_ticks()
  else:
    #display victory image
    s.blit(victory_img, (360, 150))
    if pygame.time.get_ticks()-round_over_time>round_cooldown:
      round_over = False
      intro_count = 3
      Knight= Knight(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
      Assassin= Assassin(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)

  #event handler
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      run = False

  #update display
  pygame.display.update()

#exit pygame
pygame.quit()`

My Module-

import pygame

class Characters():
  def __init__(self, player, x, y, flip, data, sheet, steps):
    self.player = player
    self.size = data[0]
    self.image_scale = data[1]
    self.offset = data[2]
    self.flip = flip
    self.animation_list = self.load_images(sheet, steps)
    self.action = 0#0:idle #1:run #2:jump #3:attack1 #4: attack2 #5:hit #6:death
    self.frame_index = 0
    self.image = self.animation_list[self.action][self.frame_index]
    self.update_time = pygame.time.get_ticks()
    self.rect = pygame.Rect((x, y, 80, 180))
    self.vel_y = 0
    self.running = False
    self.jump = False
    self.attacking = False
    self.attack_type = 0
    self.attack_cooldown = 0
    self.hit = False
    self.health = 100
    self.alive = True


  def load_images(self, sheet, steps):
    #extract images from spritesheet
    animation_list = []
    for y, animation in enumerate(steps):
      temp_img_list = []
      for x in range(animation):
        temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
        temp_img_list.append(pygame.transform.scale(temp_img, (self.size * self.image_scale, self.size * self.image_scale)))
      animation_list.append(temp_img_list)
    return animation_list


  def move(self, screen_width, screen_height, surface, target, round_over):
    SPEED = 10
    GRAVITY = 2
    dx = 0
    dy = 0
    self.running = False
    self.attack_type = 0

    #get keypresses
    key = pygame.key.get_pressed()

    #can only perform other actions if not currently attacking
    if self.attacking == False and self.alive == True and round_over == False:
      #check player 1 controls
      if self.player == 1:
        #movement
        if key[pygame.K_a]:
          dx = -SPEED
          self.running = True
        if key[pygame.K_d]:
          dx = SPEED
          self.running = True
        #jump
        if key[pygame.K_w] and self.jump == False:
          self.vel_y = -30
          self.jump = True
        #attack
        if key[pygame.K_r] or key[pygame.K_t]:
          self.attack(target)
          #determine which attack type was used
          if key[pygame.K_r]:
            self.attack_type = 1
          if key[pygame.K_t]:
            self.attack_type = 2


      #check player 2 controls
      if self.player == 2:
        #movement
        if key[pygame.K_LEFT]:
          dx = -SPEED
          self.running = True
        if key[pygame.K_RIGHT]:
          dx = SPEED
          self.running = True
        #jump
        if key[pygame.K_UP] and self.jump == False:
          self.vel_y = -30
          self.jump = True
        #attack
        if key[pygame.K_KP1] or key[pygame.K_KP2]:
          self.attack(target)
          #determine which attack type was used
          if key[pygame.K_KP1]:
            self.attack_type = 1
          if key[pygame.K_KP2]:
            self.attack_type = 2


    #apply gravity
    self.vel_y += GRAVITY
    dy += self.vel_y

    #ensure player stays on screen
    if self.rect.left + dx < 0:
      dx = -self.rect.left
    if self.rect.right + dx > screen_width:
      dx = screen_width - self.rect.right
    if self.rect.bottom + dy > screen_height - 220:
      self.vel_y = 0
      self.jump = False
      dy = screen_height - 220 - self.rect.bottom

    #ensure players face each other
    if target.rect.centerx > self.rect.centerx:
      self.flip = False
    else:
      self.flip = True

    #apply attack cooldown
    if self.attack_cooldown > 0:
      self.attack_cooldown -= 1

    #update player position
    self.rect.x += dx
    self.rect.y += dy


  #handle animation updates
  def update(self):
    #check what action the player is performing
    if self.health <= 0:
      self.health = 0
      self.alive = False
      self.update_action(6)#6:death
    elif self.hit == True:
      self.update_action(5)#5:hit
    elif self.attacking == True:
      if self.attack_type == 1:
        self.update_action(3)#3:attack1
      elif self.attack_type == 2:
        self.update_action(4)#4:attack2
    elif self.jump == True:
      self.update_action(2)#2:jump
    elif self.running == True:
      self.update_action(1)#1:run
    else:
      self.update_action(0)#0:idle

    animation_cooldown = 50
    #update image
    self.image = self.animation_list[self.action][self.frame_index]
    #check if enough time has passed since the last update
    if pygame.time.get_ticks() - self.update_time > animation_cooldown:
      self.frame_index += 1
      self.update_time = pygame.time.get_ticks()
    #check if the animation has finished
    if self.frame_index >= len(self.animation_list[self.action]):
      #if the player is dead then end the animation
      if self.alive == False:
        self.frame_index = len(self.animation_list[self.action]) - 1
      else:
        self.frame_index = 0
        #check if an attack was executed
        if self.action == 3 or self.action == 4:
          self.attacking = False
          self.attack_cooldown = 20
        #check if damage was taken
        if self.action == 5:
          self.hit = False
          #if the player was in the middle of an attack, then the attack is stopped
          self.attacking = False
          self.attack_cooldown = 20


  def attack(self, target):
    if self.attack_cooldown == 0:
      #execute attack
      self.attacking = True
      self.attack_sound.play()
      attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
      if attacking_rect.colliderect(target.rect):
        target.health -= 10
        target.hit = True


  def update_action(self, new_action):
    #check if the new action is different to the previous one
    if new_action != self.action:
      self.action = new_action
      
      #update the animation settings
      self.frame_index = 0
      self.update_time = pygame.time.get_ticks()

  def draw(self, surface):
    img = pygame.transform.flip(self.image, self.flip, False)
    surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))

The error occurred-

File "c:\Users\User\Desktop\G1\Battle game V1.py", line 73, in <module>
    Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
  File "c:\Users\User\Desktop\G1\Players.py", line 10, in __init__       
    self.animation_list = self.load_images(sheet, steps)
  File "c:\Users\User\Desktop\G1\Players.py", line 33, in load_images    
    temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
ValueError: subsurface rectangle outside surface area
  • What do you not understand? The error message is very clear. You are trying to create a `subsurface` from an area that is not completely on the surface (`sheet`). This does not work. You have to examine the coordinates you use in `subsurface`. But nobody can help you with that. – Rabbid76 Dec 13 '22 at 18:13
  • `I would also like help to make a menu and a player section system.` we can't help you with that. Your posts cna only be about one question, and your questions have to be specific, not broad. That's very broad. The [mre], [ask] and [help] pages ought to help you understand what is required when asking questions on this site. – Random Davis Dec 13 '22 at 18:19
  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. – Community Dec 13 '22 at 18:37

0 Answers0