Reputation: 603
Okay, so I'm making a little game with pygame and building the map by generating tiles to a multidimensional array. In order to do that I'm using two for loops.
def create(this, t):
if t == "grasslands":
for j in range(0, this.numRows):
for i in range(0, this.numColumns):
this.column.append(this.Tile("grass", j * this.tileWidth, i * this.tileHeight))
this.row.append(this.column)
The value for j * this.tileWidth is getting passed into the Tile initialization correctly. After though the column[whatever].x value is still 0. The y value gets set just fine, and if I use i or any other value instead of j things work just fine. Is this something I'm doing wrong or something wonky with Python?
mapgen.py
import pygame
from sprite import *
from assets import *
class mapG:
def __init__(this, resw, resh):
this.numRows = 3
this.numcolumns = 3
this.tileWidth = 128
this.tileHeight = 128
this.row = []
this.column = []
this.width = this.numRows * this.tileWidth
this.height = this.numcolumns * this.tileHeight
def create(this, t):
if t == "grasslands":
for j in range(0, this.numRows):
for i in range(0, this.numcolumns):
this.column.append(this.Tile("grass", j * this.tileWidth, i * this.tileHeight))
this.row.append(this.column)
def tileAt(this, x, y):
pass
def moveRight(this):
for j in range(0,this.numRows):
for i in range(0, this.numcolumns):
this.row[j][i].incX(1)
def Update(this, src):
for j in range(0,this.numRows):
for i in range(0, this.numcolumns):
this.row[j][i].Update(src)
print(this.row[j][i].y, this.row[j][i].x)
class Tile:
def __init__(this, name, xpos, ypos):
this.y = ypos
this.x = xpos
this.image = assets.tile[name + ".png"]
this.sprite = sprite(this.image, this.x, this.y, 100, 100)
def incX(this, amount):
this.sprite.IncX(amount)
def decX(this, amount):
this.sprite.DecX(amount)
def incY(this, amount):
this.sprite.IncY(amount)
def decY(this, amount):
this.sprite.DecY(amount)
def Update(this, src = None):
if src != None:
this.sprite.Update(src)
sprite.py
import pygame
import assets
class sprite:
def __init__(this, image, xpos, ypos, width = None, height = None):
this.image = image
this.x = xpos
this.y = ypos
this.width = width
this.height = height
if this.width != None and this.height != None:
this.image = pygame.transform.scale(image, (this.width,this.height))
def GetPos(this):
return (this.x, this.y)
def IncX(this, amount):
this.x += amount
def IncY(this, amount):
this.y += amount
def DecX(this, amount):
this.x -= amount
def DecY(this, amount):
this.y -= amount
def Update(this, src = None):
if src != None:
src.blit(this.image, this.GetPos())
Upvotes: 0
Views: 202
Reputation: 3171
You don't end up creating rows of width 3 (numColumns
). You end up with rows of width 9 (rows*cols) since you don't create a new row each time through the loop.
Your loop should look like this:
for j in range(0, this.numRows):
row=[]
for i in range(0, this.numColumns):
row.append(this.Tile("grass", j * this.tileWidth, i * this.tileHeight))
this.rows.append(row)
And then you probably ended up looking at the wrong data when you were debugging it, not realizing the row was longer than you expected. (Also the names are a bit confusing, so I changed them a bit.)
Upvotes: 0
Reputation: 104722
I believe the issue you have comes from your use of the this.column
variable in the create
method.
You're only creating one column list (in __init__
) and then reusing it for all the columns of your map. This won't work.
Your this.row
list ends up having multiple references to the same column list, which ends up containing all of the Tile
objects you create. You only see some of them later, since your iteration code uses predefined dimensions, rather than actually iterating over the whole of the lists.
To understand this, try to imagine how the iteration progresses for a 2x2 grid (ignoring the tile dimensions). I'm putting each i
and j
value on it's own line to show how it progresses, and giving the values of row
and columns
after each step side:
j=0:
i=0:
column.append(Tile(i, j))
# column is [Tile(0, 0)]
# row is []
i=1:
column.append(Tile(i, j))
# column is [Tile(0, 0), Tile(0, 1)]
# row is []
row.append(column)
# column is [Tile(0, 0), Tile(0, 1)]
# row is [[Tile(0, 0), Tile(0, 1)]]
j=1: # column is not reset!
i=0:
column.append(Tile(i, j))
# column is [Tile(0, 0), Tile(0, 1), Tile(1, 0)]
# row is [[Tile(0, 0), Tile(0, 1), Tile(1, 0)]]
i=1:
column.append(Tile(i, j))
# column is [Tile(0, 0), Tile(0, 1), Tile(1, 0), Tile(1, 1)]
# row is [[Tile(0, 0), Tile(0, 1), Tile(1, 0), Tile(1, 1)]]
row.append(column)
# column is [Tile(0, 0), Tile(0, 1), Tile(1, 0), Tile(1, 1)]
# row is [[Tile(0, 0), Tile(0, 1), Tile(1, 0), Tile(1, 1)],
# [Tile(0, 0), Tile(0, 1), Tile(1, 0), Tile(1, 1)]]
The row
list contains two references to the same column
list of four tiles. Your code had intended to add the first two Tile(0, 0)
and Tile(0,1)
to the first column, then the last two tiles Tile(1, 0)
and Tile(1, 1)
to the second column. But because the same list was used both times, you end up with all the values together, and then the whole bunch repeated. When you iterate, you're only seeing the repeated values on the left part of the diagram above.
Here's how to fix it:
def create(this, t):
if t == "grasslands":
for j in range(0, this.numRows):
column = [] # Create a new list! This is the key!
for i in range(0, this.numColumns):
column.append(this.Tile("grass",
j * this.tileWidth,
i * this.tileHeight))
this.row.append(column)
You can get rid of the line in the constructor that initializes self.column
too. It's only needed temporarily, so there's no need to use an instance variable.
Upvotes: 5