【问题标题】:How to fix "IndexError: List index is out of range" in my code?如何在我的代码中修复“IndexError:列表索引超出范围”?
【发布时间】:2019-10-14 17:10:43
【问题描述】:

我对 Python 很陌生,我想测试一个 tilemap 系统。当我尝试加载图块时,我收到错误消息IndexError: list index out of range。为了解决这个问题,我需要做些什么,以及将来其他人喜欢它? 确切的错误:

Traceback (most recent call last):
  File "N:/Python/Projects/Projects/First Topdown/First Topdown.py", line 114, in <module>
    pygame.draw.rect(screen, colors[tilemap[row][column]], (column*tilesize, row*tilesize, tilesize, tilesize))
IndexError: list index out of range

我听说这意味着我正在尝试访问 none 或空的值。 这是关于tilemap 的代码部分。没有最后一个绘图位,其余的工作正常。 (这不是很多其他代码,只是运动。)

import pygame

pygame.init()

tilesize = 64
mapwidth = 16
mapheight = 13
screen = pygame.display.set_mode((mapwidth*tilesize, mapheight*tilesize))


pygame.display.set_caption("Aspen")

WALLTOP = 0
WALLBOT = 1
GRASS = 2
wallTop = (128, 128, 128)
wallBot = (210, 105, 30,)
grass =   (50, 205, 50)

colors = {
WALLTOP : wallTop,
WALLBOT: wallBot,
GRASS : grass
}

tilemap = [
            [WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP,
            WALLTOP, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, WALLTOP,
            WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP]
]


running = True
while running:

    screen.fill((64, 64, 64))

    for row in range(mapheight):
        for column in range(mapwidth):
            pygame.draw.rect(screen, colors[tilemap[row][column]], (column*tilesize, row*tilesize, tilesize, tilesize))

    pygame.display.update()

【问题讨论】:

  • 我不太明白...你有四个元素,python 从零开始索引容器。所以你有索引0-3。 4 不存在。当你迭代索引时,我猜你必须记住这一点,但通常你使用range(len(Object)),因为 range 函数默认产生从 0 到 len(Object)-1 的数字...
  • 添加编译器给你的准确的错误信息会很有帮助
  • 已添加完整的错误消息

标签: python list indexing


【解决方案1】:

您获得了IndexError,因为您只在tilemap 中定义了一个 行,但外部for 循环提供了从0 到@ 的row 索引987654327@因为这个声明:

for row in range(mapheight):

为避免该错误,请尝试将其更改为:

for row in range(len(tilemap)):

更新

我想我已经弄清楚你想要做什么了。正如我之前所说,IndexError 是因为tilemap 中的行数与您尝试使用嵌套的for 循环显示的行数不匹配。

下面我已经修复了 tilemap 的定义方式,因此它具有适当数量的行和列(还简化了一些其他定义,并在显示循环中添加了一些事件处理以允许脚本正常关闭而不是在无限循环中运行)。

import pygame

pygame.init()

tilesize = 64
mapwidth, mapheight = 16, 13

screen = pygame.display.set_mode((mapwidth*tilesize, mapheight*tilesize))

pygame.display.set_caption("Aspen")

WALLTOP, WALLBOT, GRASS = range(3)

wallTop = (128, 128, 128)
wallBot = (210, 105, 30,)
grass =   (50, 205, 50)

colors = {
    WALLTOP : wallTop,
    WALLBOT : wallBot,
    GRASS   : grass
}

tilemap = [
    [WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP],
    [WALLTOP, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLBOT, WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   GRASS,   WALLTOP],
    [WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP, WALLTOP],
]

running = True
while running:

    screen.fill((64, 64, 64))

    for row in range(mapheight):
        for column in range(mapwidth):
            pygame.draw.rect(screen, colors[tilemap[row][column]], (column*tilesize, row*tilesize, tilesize, tilesize))

    # Process user-events.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:  # User clicked to close window.
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE: # Press escape key to quit.
                running = False

    pygame.display.update()

【讨论】:

  • 嘿!这有点奏效。我没有错误了,这很好。但是,只绘制了第一行,我无法弄清楚为什么没有绘制图像的其余部分。
  • Zukane:很高兴听到……在这种情况下,请接受我的回答。见What should I do when someone answers my question?。如果您还有其他问题,请单独发布并发布新问题。
【解决方案2】:

好吧,我终于明白你在做什么了。

您尝试制作瓷砖地图的第二个切片,即使您的瓷砖地图是一维的。对于多维数组,最好使用 numpy 数组 imo。当您在我的代码中打印出 tmap 时,您会明白我的意思,它更具可读性。此外,由于 python 使用缩进,因此通过将不同的参数分隔到不同的行来组织长函数调用是一个好主意。否则,您的代码中会出现非常难以阅读的长行。

import pygame
import numpy as np

pygame.init()

# Define base values for tiles
tsize, width, height = 64,16,13

# Set the stage with an empty 2D array full of zeroes
# Create the tilemap dynamically from width and height.
tmap = np.zeros((height,width),dtype=int)

# defines the screen I guess?
screen = pygame.display.set_mode((width*tsize, height*tsize))
pygame.display.set_caption("Asspain")

# You can now change the tilemap manually.
# You have three unique rows, so you can
# just make lists of those and later insert
# them into your np.array
r1 = [1]*width

r2 = [1]
r2.extend([2]*14)
r2.extend([1])

r3 = [1]
r3.extend([0]*14)
r3.extend([1])

# turn your lists into np.arrays so they work
# with your tmap.
r1, r2, r3 = np.array(r1),np.array(r2),np.array(r3)

# insert the rows at the appropreate positions
# mind you, indexing works a bit different in
# np.arrays than in python builtin lists.
tmap[0] = r1.copy()
tmap[1] = r2.copy()
tmap[2:12] = r3.copy()
tmap[12] = r1.copy()

# You now have a nice and readable tmap.
# You can make the map creation process
# as manual as you want, I just used shortcuts
# based on symmetry.
print(tmap)

# Now you can just iterate over every element
# via double iteration, or using indices.
# I've heard iterating over indices using range
# is always faster.

# define colormap
cmap = {0:(50, 205, 50),
        1:(128, 128, 128),
        2:(210, 105, 30,)}

# this is unneccessary, but it's good to know
# that numpy arrays also have a len() like
# attribute called shape.
width, heigth = tmap.shape[0], tmap.shape[1]

# Gameloop
while True:

    # Is this the default color of a tile?
    # Do you need that?
    screen.fill((64, 64, 64))

    for row in range(width):
        for col in range(heigth):
            pygame.draw.rect(screen, 
                             cmap[tmap[row][col]],
                             (col*tsize,
                              row*tsize,
                              tsize,
                              tsize))
    pygame.display.update()

编辑:我注意到你做了很多字典切片,它们非常慢。如果您不需要在游戏循环中重复切片字典(即地图不会更改),请不要。我编写了一些额外的代码,它生成了与上面相同的 tmap 数组作为图块的模板,但是这一次,我还要转换一个数组,它将颜色三元组映射到这些位置。

通过这种方式,您可以使用快速 numpy 数组创建整个地图,该数组利用数组的 dtype 类型转换,同时仍受益于先前指定的 tmap 的可读性。

import numpy as np
import matplotlib.pyplot as plt

tsize, width, height = 64,16,13
tmap = np.zeros((height,width),dtype=int)
cmap = np.zeros((height,width,4),dtype=int)

r1 = [1]*width

r2 = [1]
r2.extend([2]*14)
r2.extend([1])

r3 = [1]
r3.extend([0]*14)
r3.extend([1])

r1, r2, r3 = np.array(r1),np.array(r2),np.array(r3)

tmap[0] = r1.copy()
tmap[1] = r2.copy()
tmap[2:12] = r3.copy()
tmap[12] = r1.copy()

cdct = {0:np.array((50, 205, 50, 200),dtype=int),
        1:np.array((128, 128, 128, 200),dtype=int),
        2:np.array((210, 105, 30, 200),dtype=int)}

for posy, row in enumerate(tmap):
    for posx, col in enumerate(row):
        cmap[posy,posx] = cdct[col].copy()

fig, ax = plt.subplots()
plt.axis('off')

ax.imshow(cmap)

如您所见,matplotlib 可以轻松可视化 X、Y、Z 数组,imshow() 函数可以将其读取为位图。 X 是行,Y 是列,Z 是颜色三元组。实际上,我将 Z 设为四个元素,以便用第四个元素切换透明度。这样,您可以将位图创建一次作为背景,然后直接在颜色四元组上进行迭代,而无需在 while 循环的每次迭代中执行 16*13 慢速字典切片。显然,绘图只是对其外观的视觉演示,并且是一个用于建模游戏地图的漂亮小工具。

而且由于它使用类型转换的 numpy 数组并避免了许多缓慢的操作,因此与以前相比应该快如闪电。

【讨论】:

  • 是的,我理解这段代码。以前没有听说过 numpy,我将对其进行更多研究并将其实现到我的代码中。非常感谢!
  • 另外,我刚刚意识到:常量字典切片很慢。 dicts 通常非常慢,因此在循环中一遍又一遍地切片它们会严重影响性能。这通常是 python 的一个问题,因为它不使用内置级别的类型转换。 Numpy 数组可以使用 dtype 进行转换,这使得它们更快。如果您的 tmap 没有改变,最好只在 while 循环之外创建一次,并且颜色三元组已经在正确的位置。
猜你喜欢
  • 1970-01-01
  • 2019-09-15
  • 2019-10-02
  • 1970-01-01
  • 1970-01-01
  • 2019-01-06
  • 2011-10-31
  • 2015-06-26
相关资源
最近更新 更多