Search code examples
pythonlistpyglet

TypeError: unhashable type: 'list' In pyglet


So after getting inspired by code bullet to try out pyglet. Recently I have been creating many small games and simulations with it. The current one being a game is a flappy bird(Pretty sure you've all played that). So my mission is to code flappybird with pyglet today. But as usual, I fell into a problem, and even though there were many questions and answers for this problem as I was using pyglet all of the other answers weren't working. The code I have written for the flappy bird is pretty straightforward. I have 2 classes the bird and the pipe and I render all of them using batches and groups. here:

import math, sys
import pyglet, random
from pyglet.window import key, mouse

window = pyglet.window.Window(width=335, height=540, caption="FLAPPY BIRD!")

batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
pipes = pyglet.graphics.OrderedGroup(1)
foreground = pyglet.graphics.OrderedGroup(2)
player = pyglet.graphics.OrderedGroup(3)

bg = pyglet.image.load("background.png")
background_1 = pyglet.sprite.Sprite(bg, 0, 0, batch=batch, group=background)
background_2 = pyglet.sprite.Sprite(bg, bg.width, 0, batch=batch, group=background)

base = pyglet.image.load("base.png")
base_1 = pyglet.sprite.Sprite(base, 0, 0, batch=batch, group=foreground)
base_2 = pyglet.sprite.Sprite(base, base.width, 0, batch=batch, group=foreground)

bird_image = pyglet.image.load("yellowbird-midflap.png")
bird_image.anchor_x = bird_image.width // 2
bird_image.anchor_y = bird_image.height // 2

class Bird:
    def __init__(self):
        self.charector = \
            pyglet.sprite.Sprite(bird_image, window.width * 0.2, window.height/2, batch=batch, group=player)
        self.y_speed = 0
        self.rotation_vel = 0
        self.alive = True
    
    def update(self):
        self.y_speed -= 0.6
        self.rotation_vel += 1
        self.charector.y += self.y_speed
        self.charector.rotation = min(self.rotation_vel, 90)
    
    def jump(self):
        self.y_speed = 7 * 1.5
        self.rotation_vel = -35

bird_image = pyglet.image.load("yellowbird-midflap.png")

class Pipe:
    tp = pyglet.image.load("down-pipe.png")
    bp = pyglet.image.load("pipe.png")
    
    def __init__(self):
        self.top_pipe = pyglet.sprite.Sprite(self.tp, x = window.width + 100, y = random.randint(325, 484), batch=batch, group=pipes)
        self.bottom_pipe = pyglet.sprite.Sprite(self.bp, x = window.width + 100, y = self.top_pipe.y - 125 - self.bp.height, batch=batch, group=pipes)
    
    def update(self):
        self.top_pipe.x -= 3
        self.bottom_pipe.x -= 3

bird = Bird()
pipes = [Pipe()]
time_created_pipe = 50

def update_char(dt):
    global time_created_pipe,  pipes

    if bird.alive:
        bird.update()
        for pipe in pipes:
            pipe.update()
            if pipe.top_pipe.x <= -100:
                pipes.remove(pipe)
            
        if time_created_pipe <= 0:
            time_created_pipe = 50
            pipes.append(Pipe())
        time_created_pipe -= 1


def update_bg(dt):
    if bird.alive:
        background_1.x -= 0.5
        background_2.x -= 0.5
        if background_1.x <= -bg.width:
            background_1.x = bg.width
        if background_2.x <= -bg.width:
            background_2.x = bg.width
        
        base_1.x -= 1.5
        base_2.x -= 1.5
        if base_1.x <= -base.width:
            base_1.x = base.width
        if base_2.x <= -base.width:
            base_2.x = base.width

@window.event
def on_mouse_press(x, y, button, modifiers):
    if button == mouse.LEFT:
        bird.jump()

@window.event
def on_draw():
    window.clear()
    batch.draw()

pyglet.clock.schedule_interval(update_char, 1/60)
pyglet.clock.schedule_interval(update_bg, 1/60)
pyglet.app.run()

But the second I run the code this happens:

back (most recent call last):
File "c:\Users\Tejas&Shiva\OneDrive\Desktop\HTCODEATHON PYTHON PRACTICE\Day4\flappy bird.py", line 105, in <module>
    pyglet.app.run()
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\app\__init__.py", line 107, in run
    event_loop.run()
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\app\base.py", line 169, in run
    timeout = self.idle()
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\app\base.py", line 239, in idle
    redraw_all = self.clock.call_scheduled_functions(dt)
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\clock.py", line 292, in call_scheduled_functions
    item.func(now - item.last_ts, *item.args, **item.kwargs)
File "c:\Users\Tejas&Shiva\OneDrive\Desktop\HTCODEATHON PYTHON PRACTICE\Day4\flappy bird.py", line 73, in update_char
    pipes.append(Pipe())
File "c:\Users\Tejas&Shiva\OneDrive\Desktop\HTCODEATHON PYTHON PRACTICE\Day4\flappy bird.py", line 50, in __init__
    self.top_pipe = pyglet.sprite.Sprite(self.tp, x = window.width + 100, y = random.randint(325, 484), batch=batch, group=pipes)
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\sprite.py", line 246, in __init__
    self._create_vertex_list()
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\sprite.py", line 391, in _create_vertex_list
    self._vertex_list = self._batch.add(
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\graphics\__init__.py", line 366, in add
    domain = self._get_domain(False, mode, group, formats)
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\graphics\__init__.py", line 443, in _get_domain
    self._add_group(group)
File "C:\Users\Tejas&Shiva\AppData\Local\Programs\Python\Python310\lib\site-packages\pyglet\graphics\__init__.py", line 468, in _add_group
    if group.parent not in self.group_map:
TypeError: unhashable type: 'list'

My guess is there is a problem with the pipes list but I'm not sure why. Is there any solution to this?


Solution

  • The problem is that the name pipes is used twice. First is used for the OrderedGroup of pipes. In this group, the initial pipe batches are added:

    pipes = pyglet.graphics.OrderedGroup(1)
    

    However, it is then used for a list of pipes. The original group pipes is shadowed by the list of pipes and creating a new Pipe object fails:

    pipes = [Pipe()]
    

    Use different names for the group and the list. Rename list:

    pipes_list = [Pipe()]
    time_created_pipe = 50
    
    def update_char(dt):
        global time_created_pipe, pipes_list
    
        if bird.alive:
            bird.update()
            for pipe in pipes_list:
                pipe.update()
                if pipe.top_pipe.x <= -100:
                    pipes_list.remove(pipe)
                
            if time_created_pipe <= 0:
                time_created_pipe = 50
                pipes_list.append(Pipe())
            time_created_pipe -= 1