Why this lua function is modifying tables it shouldn't? - lua

I have this function in my code to load and setup sizes for sprites.
function aux.Sprite:setTexture(renderer,imgPath)
... -- Not important for this question
img = loadImage(renderer,imgPath)
self.texture = img.texture
self.rect.w = img.w
self.rect.h = img.h
end
(loadImage here is the function implemented in C, and is returning the correct values)
Using it should be easy enough
bg = aux.Sprite:new()
bg:setTexture(R, "testfiles/bg.png")
ship = aux.Sprite:new()
ship:setTexture(R, "testfiles/testship.png")
The problem is that after the second call for setTexture the values for the FIRST sprite is changed!
for example
bg = aux.Sprite:new()
bg:setTexture(R, "testfiles/bg.png")
print(bg.rect.w)
ship = aux.Sprite:new()
ship:setTexture(R, "testfiles/testship.png")
print(bg.rect.w)
should return
1920 1920
because I'm printing the width for bg twice
but I'm getting
1920
300
That is, the second setTexture changes the value for "bg" and not only for "ship".
My guess is that self.rect.w = img.w is setting a "pointer", or whatever is called in lua, to img.w and when I use the function later this pointer is updated in all references?
What I'm doing wrong here? Is this the correct lua behavior?
PS: The definition of the Sprite:new function as asked
function aux.Sprite:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end

There is nothing in the provided code that actually creates the rect (or aux.Sprite for that matter). I guess this means that is done via something like
aux.Sprite = { rect = {} }
This is a problem because it means all your sprites share the same rect.
aux.Sprite:new() returns a new empty table that has the metatable and its __index set to Sprite. Thus when in setTexture self.rect is searched in this empty table the one from Sprite is returned via __index.
You need to make sure every sprite has its own unique rect.
I don't really know what is the typical Lua object pattern here, but you can eg. have self.rect = { w = img.w, h = img.h } in setTexture or maybe o.rect = {} in new - something that actually sets rect to a new table for this particular sprite.

Related

Drawing a Matrix

Im trying to generate a random map using a matrix but I dont really know how. Here is the
function for the matrix. wMap and hMap are the width and height, and mapSprites is a table containing some ground sprites. Also how can I draw the matrix? Im sorry if this is too much of a question, but Im really in need for some help
function buildMap(wMap, hMap)
for i = 1, wMap do
mt[i] = {}
for j = 1, hMap do
mt[i][j] = math.random(mapSprites)
end
end
end
Generating a random map in any programming language will utilize two core concepts: The language's random function and nested for loops, two for the case of a map/matrix/2d array.
The first problem, is you may or may not have mt initialized outside the function. This function assumes the variable exists outside of the function and each time the function is called it will overwrite mt (or initialize it for the first function call) with random values.
The second problem, the width, wMap, and height, hMap, of the map are in the wrong order, as maps/matrices/2d arrays first iterate over the height (y dimension) and then the width (x dimension).
The last problem, mapSpripes also has to be declared outside the function (which is not clear with your code snippet), which will be the highest possible value the random function can generate. You can read more about math.random here: http://lua-users.org/wiki/MathLibraryTutorial
Consider this function I wrote that makes those adjustments as well as has some additional variables for the minimum and maximum random value. Of course, you can remove these to have it fit your intended purposes.
function buildMap(wMap, hMap)
local minRand = 10
local maxRand = 20
for y = 1, hMap do
matrix[y] = {}
for x = 1, wMap do
matrix[y][x] = math.random(minRand, maxRand)
end
end
end
I suggest you use this function as inspiration for your future iteratins. You can make minRand and maxRand parameters or make matrix a returned value rather than manipulating an already declared matrix value outside of the function.
Best of luck!
EDIT:
Regarding your second question. Look back at the section I wrote about nested for loops. This will be crucial to "drawing" your map. I believe you have the building blocks to resolve this issue yourself as there isn't enough context provided about what "drawing" looks like. Here is a fundamentally similiar function, based on my previous function, on printing the map:
function printMap(matrix)
for i = 1, #matrix do
for j = 1, #matrix[i] do
io.write(matrix[i][j] .. " ")
end
io.write("\n")
end
end
For choosing random sprite, I recommend you to create a table of sprites and then save index of sprite in matrix. Then you can draw it in same loop, but now, you will iterate over matrix and draw sprite based on sprite index saved in matrix in position given by matrix position (x and y in loop) times size of sprite.
local sprites, mt = {}, {}
local spriteWidth, spriteHeight = 16, 16 -- Width and height of sprites
function buildMap(wMap, hMap)
mt = {}
for i = 1, wMap do
mt[i] = {}
for j = 1, hMap do
mt[i][j] = math.random(#sprites) -- We choose random sprite index (#sprites is length of sprites table)
end
end
end
function love.load()
sprites = {
love.graphics.newImage('sprite1.png'),
love.graphics.newImage('sprite2.png'),
-- ...
}
buildMap()
end
function love.draw()
for y, row in ipairs(mt) do
for x, spriteIndex in ipairs(row) do
-- x - 1, because we want to start at 0, 0, but lua table indexing starts at 1
love.graphics.draw(sprites[spriteIndex], (x - 1) * spriteWidth, (y - 1) * spriteHeight)
end
end
end

Lua get table that function is attached to

Hello I want to get the table that a function is attached to, I can't really find a nice way to explain it but I think i've explained it good enough within the code for. Basically I need to get the table that the function is attached to from another function, without passing in the table.
function DrawRect()
print(debug.getinfo(1).name) -- this gets the name of the function that is invoking DrawRect ('Paint')..
-- I want to be able to get the table that is attached to this function
-- So I can do table.x inside this function, and have it print 123
end
local r = math.random(1, 100)
_G["abc" .. r] = {
x = 123,
Paint = function(self)
DrawRect()
end
}
_G["abc" .. r]:Paint()
Example of the problem i'm trying to solve
This is my current code right now
function DrawRect(x,y,w,h)
draw.DrawRect(x,y,w,h)
end
local Button = {
Init = function(self)
self.label = gui.Label("Button")
self.label:SetPos(10, 5) -- see the position is relative to the Button's position
self.label:SetColor(255,255,255)
end,
Paint = function(self,x,y,w,h)
Color(40,40,40)
DrawRect(x,y,w,h) -- Draws dark background
end
}
As you can see paint has 4 args, x,y,w,h. I want to do away with x,y and only have w,h. I want to achieve this like this.
function DrawRect(x,y,w,h)
local relative_x = parent_table_of_paint.INTERNAL.draw_x
local relative_y = parent_table_of_paint.INTERNAL.draw_y
draw.DrawRect(relative_x + x, relative_y + y,w,h)
end
local Button = {
Init = function(self)
self.label = gui.Label("Button")
self.label:SetPos(10, 5) -- see the position is relative to the Button's position
self.label:SetColor(255,255,255)
end,
Paint = function(self,w,h)
Color(40,40,40)
DrawRect(0,0,w,h) -- Draws dark background
end
}
I know you can't see some of the properties in my example, but they are there.
Edit 2:
I am recreating the a frame, "VGUI".
https://wiki.facepunch.com/gmod/draw.RoundedBox
draw.RoundedBox( number cornerRadius, number x, number y, number width, number height, table color )
and as you can see this has the functionality I want
https://wiki.facepunch.com/gmod/PANEL:Paint
local panel = vgui.Create( "DPanel" )
panel:SetSize( 100, 100 )
panel:SetPos( ScrW() / 2 - 50, ScrH() / 2 - 50 )
function panel:Paint( w, h )
draw.RoundedBox( 8, 0, 0, w, h, Color( 0, 0, 0 ) )
end
A function is a first class value, so there may be many tables and variables that have a reference to the same function. There is no way for that function to know what those tables and variables are.
That's not how you would implement something like that. You don't implement a function that through some magic gets its caller's container so it can access its other values.
DrawRect should just draw a rect. If you want to draw a rect somewhere else you should provide that offset through the parameters of DrawRect.
I modified your button so it will simply put the buttons x and y coordinates (if they exist) into DrawRect
local Button = {
Init = function(self)
self.label = gui.Label("Button")
self.label:SetPos(10, 5) -- see the position is relative to the Button's position
self.label:SetColor(255,255,255)
end,
Paint = function(self,w,h)
Color(40,40,40)
local x = self.x or 0
local y = self.y or 0
DrawRect(x,y,w,h) -- Draws dark background
end
}
That way you can call Button:Paint(20,30) to draw a 20 by 30 rectangle at the buttons coordinates. If you want to add an offset, do that outside of DrawRect
# Edit 2:
This is something different. The host program will call the Paint function.
vgui.Create( "DPanel" ) will return a new panel instance and the game will add a reference to it to a list. Everytime the gui is updated it will call the Paint functions of all the panels in that list. That's how the function knows width and height of the panel.

Getting data from a table

Using Tiled I generated a Lua file which contains a table. So I figured that I'd write a for loop which cycles through the table gets the tile id and checks if collision is true and add collision if it was. But, I've been unable to get the tile id's or check they're properties. But it returned a error saying that I tried to index nil value tileData.
Here is the Map file
return {
version = "1.1",
luaversion = "5.1",
-- more misc. data
tilesets = {
{
name = "Tileset1",
firstgid = 1,
tilewidth = 16,
tileheight = 16,
tiles = {
{
id = 0,
properties = {
["Collision"] = false
}
},
}
}
layers = {
{
type = "tilelayer",
name = "Tile Layer 1"
data = {
-- array of tile id's
}
}
}
}
And here is the for loop I wrote to cycle through the table
require("Protyping")
local map = love.filesystem.load("Protyping.lua")()
local tileset1 = map.tilesets
local tileData = tileset1.tiles
local colision_layer = map.layers[1].data
for y=1,16 do
for x=1,16 do
if tileData[colision_layer[x*y]].properties["Colision"] == true then
world:add("collider "..x*y,x*map.tilewidth, y*tileheight,tilewidth,tileheight)
end
end
end
Try this:
tileset1 = map.tilesets[1]
instead of
tileset1 = map.tilesets
lhf's answer (map.tilesets[1] instead of map.tilesets) fixes the error you were getting, but there are at least two other things you'll need to fix for your code to work.
The first is consistent spelling: you have a Collision property in your map data and a Colision check in your code.
The second thing you'll need to fix is the way that the individual tiles are being referenced. Tiled's layer data is made of 2-dimensional tile data laid out in a 1-dimensional array from left-to-right, starting at the top, so the index numbers look like this:
You would think you could just do x * y to get the index, but if you look closely, you'll see that this doesn't work. Instead, you have to do x + (y - 1) * width.
Or if you use zero-based x and y, it looks like this:
Personally, I prefer 0-based x and y (but as I get more comfortable with Lua, that may change, as Lua has 1-based arrays). If you do go with 0-based x and y, then the formula is x + 1 + y * width.
I happen to have just written a tutorial this morning that goes over the Tiled format and has some helper functions that do exactly this (using the 0-based formula). You may find it helpful: https://github.com/prust/sti-pg-example.
The tutorial uses Simple Tiled Implementation, which is a very nice library for working with Tiled lua files. Since you're trying to do collision, I should mention that STI has a plugins for both the bump collision library and the box2d (physics) collision library.

Accessing data inside a table inside a table gives nil value

As of now, I'm trying to draw a layered world map from a spritesheet, through Tiled (pretty useful since you can export the map you've created visually, directly in a .lua file).
Concerning this, I've encountered a problem: Tiled provides a lua file of this type, let's call it data.lua
return {
---data
tilesets = {
image = "path/to/image",
imagewidth = 2560,
imageheight = 1664,
--other data
},
layers = {
layer1 = {}, --there's data in here
layer2 = {} --there's data in here
}
}
In another file, world_map.lua, i have this
local world_map = {}
local world_data = {}
world_data = require "data"
local last_tile = (world_data.tilesets.imagewidth * world_data.tilesets.imageheight)/(world_data.tilewidth * world_data.tileheight)
--various functions which also use world_data
return world_map
now, when I run this code, I get that it's impossible to operate arithmethic operations on imagewidth because it's a nil value, so, i guess, it looks like it's not initialised. I can't understand what I'm doing wrong: the require instruction should assegnate that table (the one returned by data.lua) to the local variable and then I simply access this variable and operate on its fields. Then again, all the fields inside the nested table are initialised with their own value, but they are nil according to the interpreter. What am I doing wrong?

Attempt to index global 'player' (a nil value)

I'm getting a (a nil value) error when i try to do this :
player = display.newSprite( imageSheet, "sequenceDataPlayer"..math.random(1, 7) )
Looking at a test print :
print ("sequenceDataPlayer"..math.random(1, 7) )
It prints the data oky 'sequenceDataPlayer1'
What Im i doing wrong here ?
Your print statement is just printing the string "sequenceDataPlayer" concatenated with a random number between 1 and 7.
It took me a little while to figure out how to use sprites in Corona, but here's how I do it. I'll use Player for the variables since that's what you're using.
First I create an options variable to get the frames from my Player.lua file:
optionsPlayer =
{
frames = require("player").frames,
}
Then I create a variable for the image sheet:
playerSheet = graphics.newImageSheet( "player.png", optionsPlayer )
After that, I create a variable to set up the name, the sequence of frames, the time it takes to play, and set how many times it will loop:
spriteOptionsPlayer = { name="Player", start=1, count=10, time=500, loopCount = 1}
Finally, I create the new sprite:
spriteInstancePlayer = display.newSprite( playerSheet, spriteOptionsPlayer )
Once I've done all this, I usually set up the x and y positions, xScale and yScale, and other properties along with adding it to a display group.
Last of all, then I play the sprite somewhere:
spriteInstancePlayer:play()
From what it looks like, you want to have 7 different sprites to choose from. Personally, I would just create seven different sprites using all of the steps above and then put them in a table.
sprites = { spriteInstancePlayer, spriteInstancePlayer2, spriteInstancePlayer3, etc.. }
Then when I wanted to play them, I would set the position and visibility and just do:
r = math,random(1, 7)
sprites[r].x = x position
sprites[r].y = y position
sprites[r].isVisible = true
sprites[r]:play()
Of course, then I would want to set listeners to either completely remove the sprite or set the visibility to false when it's done playing, there's a collision(you'd have to add a physics body and set that all up), or whatever else might happen...
There are probably simpler ways to do it, but that's what I do.
Hope this helps.

Resources