Does Lua share tables within tables? - lua

I started developing a game with Love2d engine and Lua, and I have the following code structure.
BaseEntity = {
x = 0,
y = 0,
w = 0,
h = 0,
img = {},
}
function BaseEntity:new(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
function BaseEntity:setPos(x, y)
self.x = x
self.y = y
end
function BaseEntity:setImage( index, image )
self.img[index] = image
end
PlayerType = {["NORMAL"] = 0, ["AI"] = 1}
PlayerState = {["SELECTED"] = 0, ["NOT_SELECTED"] = 1}
Player = {
type = PlayerType.NORMAL,
state = PlayerState.NOT_SELECTED
}
Player = BaseEntity:new(Player)
function Player:new( obj )
obj = obj or BaseEntity:new()
setmetatable(obj, self)
self.__index = self
return obj
end
function Player:setImage( image )
self.img["sprite"] = image
end
When I create a few Player objects and assign different images using setImage() function to each object, they all share the same image I assigned to the last object. But when I set different positions to each object using setPos() method, they are drawn in correct distinctive positions. Why does it happen like that? Does Lua share the table img inside BaseEntity with all its instances created from it?

Tables are shared. You have to create separate instance of table if you do not want to share. Note that the x, y ... img you are defining as class variables not as instance variables. To see this, try this code:
BaseEntity = {
x = 0,
img = {},
}
function BaseEntity:new(obj)
obj = obj or {}
assert(self == BaseEntity)
setmetatable(obj, self)
-- obj.img = {}
self.__index = self
-- self.__newindex = self
return obj
end
p1 = BaseEntity:new {y = 1}
p2 = BaseEntity:new {y = 2}
print('p1:', p1.x, p1.y, p1.img)
print('p2:', p2.x, p2.y, p2.img)
print('base:', BaseEntity.x)
p1.x = 3
print('p1:', p1.x, p1.y, p1.img)
print('p2:', p2.x, p2.y, p2.img)
print('base:', BaseEntity.x)
This produces this output:
p1: 0 1 table: 0x1736430
p2: 0 2 table: 0x1736430
base: 0
p1: 3 1 table: 0x1736430
p2: 0 2 table: 0x1736430
base: 0
showing that the table is shared and that when you write to x, you are writing to the p1 instance not to the class. If now you uncomment the obj.img in new() the tables of p1 and p2 will no longer be the same: each instance will have its own. If you uncomment the __newindex line, you will see that you are then assigning to the BaseEntity "class"

Related

How to update/reload table key values

How can i update a key value using another key value as variable which is inside the same table?
local t = {}
t.playerPosition = {x = 0, y = 0, z = 0}
t.positions = {t.playerPosition.x + 1, t.playerPosition.y + 1, t.playerPosition.z + 1}
Then few lines later i update playerPosition
t.playerPosition = {x = 125, y = 50, z = 7}
And then if i print out...
result
t.positions[1] -- outputs 1
t.positions[2] -- outputs 1
t.positions[3] -- outputs 1
expected result
t.positions[1] -- outputs 126
t.positions[2] -- outputs 51
t.positions[3] -- outputs 8
As you can see key positions isnt updating, what could i do to make it possible?
t.positions = {t.playerPosition.x + 1, t.playerPosition.y + 1, t.playerPosition.z + 1}
In the above line, the expressions are evaluated once, and the resulting values are assigned to fields of the subtable. After this point, changes to the fields of t.playerPosition will not cause a reflected change in t.positions.
Metatables can be used to enable such behaviour, by dynamically calculating results when accessing the fields of t.positions.
local t = {}
t.playerPosition = { x = 0, y = 0, z = 0 }
t.positions = setmetatable({}, {
__newindex = function () return false end,
__index = function (_, index)
local lookup = { 'x', 'y', 'z' }
local value = t.playerPosition[lookup[index]]
if value then
return value + 1
end
end
})
print(t.positions[1], t.positions[2], t.positions[3])
t.playerPosition = {x = 125, y = 50, z = 7}
print(t.positions[1], t.positions[2], t.positions[3])

How to add more then one array on same "for" in lua

how can I use "for k, j in pairs() do" for 2 arrays in lua?
local blipmarker1 = {
{ x = 10 , y = 5, z = 3 },
{ x = 5, y = 5, z= 3}
}
local blipmarker2 = {
{ x = 100, y= 150, z=30 }
}
function createtext(){
local pos = GetEntityCoords(PlayerPedId(), true)
for k, j in pairs(blipmarker1,blimarker2) do
draw3DText(pos.x, pos.y, pos.z, j.x, j.y, j.z)
end
}
Function pairs() accepts only one argument of type table. You need a loop for each table:
for k,j in pairs(blipmarker1) do
...
end
for k,j in pairs(blipmarker2) do
...
end
You could write your own stateful multipairs iterator. Consult Chapter 9.3 “Coroutines as Iterators” of Programming in Lua for more details: https://www.lua.org/pil/9.3.html
local function multipairs(tables)
return coroutine.wrap(function()
for _, t in pairs(tables) do -- Maybe you want ipairs here
for k, v in pairs(t) do
coroutine.yield(k, v)
end
end
end)
end
local blipmarker1 = {
{ x = 10 , y = 5, z = 3 },
{ x = 5, y = 5, z= 3}
}
local blipmarker2 = {
{ x = 100, y= 150, z=30 }
}
for _, j in multipairs{blipmarker1, blipmarker2} do
print(j.x, j.y, j.z)
end

Xonix Game Optimization Problems

I'm doing a Xonix game on corona sdk. As a playing field, I decided to use a grid of squares. Width 44, Height 71. As a result, I received a row consisting of 3124(44*71) squares along which the player moves. But I have problems with optimization. The engine can not cope with such a number of squares on the screen at the same time. Because of this, FPS on a smartphone is not more than 12. Help me find the best solution. All that concerns polygons and meshes, I am not strong in geometry ((((
Reducing the number of squares is a bad idea. Then the appearance is lost, and the field is captured too large pieces.
Thank you in advance!
-- Creating a playing field
M.create_pole = function()
-- init
local image = display.newImage
local rect = display.newRect
local rrect = display.newRoundedRect
local floor = math.floor
M.GR_ZONE = display.newGroup()
M.POLE = {}
-- bg
M.POLE.bg = rrect(0,0,150,150,5)
M.POLE.bg.width = M.width_pole
M.POLE.bg.height = M.height_pole
M.POLE.bg.x = _W/2
M.POLE.bg.y = _H/2
M.POLE.bg.xScale = _H*.00395
M.POLE.bg.yScale = _H*.00395
M.POLE.bg:setFillColor(API.hexToCmyk(M.color.land))
M.GR:insert(M.POLE.bg)
M.POLE.img = image(IMAGES..'picture/test.jpg')
M.POLE.img.width = M.width_pole-M.size_xonix
M.POLE.img.height = M.height_pole-M.size_xonix
M.POLE.img.x = _W/2
M.POLE.img.y = _H/2
M.POLE.img.xScale = _H*.00395
M.POLE.img.yScale = _H*.00395
M.POLE.img.strokeWidth = 1.3
M.POLE.img:setStrokeColor(API.hexToCmyk(M.color.img))
M.GR:insert(M.POLE.img)
-- control player
M.POLE.bg:addEventListener( 'touch', M.swipe )
M.POLE.bg:addEventListener( 'tap', function(e)
M.POLE.player.state = 0
end )
-- arr field
M.arr_pole = {} -- newRect
local jj = 0
for i=1,M.delta_height do -- 71 point
M.arr_pole[i] = {}
M.GUI.border[i] = {}
for k=1,M.delta_width do -- 44 point
jj = jj+1
M.arr_pole[i][k] = {nm = jj}
M.GUI.border[i][k] = {}
local xx = k*M.size_xonix
local yy = i*M.size_xonix
local _x = floor(xx-M.size_xonix/2)
local _y = floor(yy-M.size_xonix/2)
M.arr_pole[i][k][1] = image(IMAGES..'pole/land.jpg')
M.arr_pole[i][k][1] .x = _x
M.arr_pole[i][k][1] .y = _y
M.GR_ZONE:insert(M.arr_pole[i][k][1])
-- water at the edges
-- the rest is land
if (i==1 or i==M.delta_height)
or (k==1 or k==M.delta_width) then
M.arr_pole[i][k].type = 0
else
M.arr_pole[i][k].type = 2
M.arr_pole[i][k][1].isVisible = true
end
end
end
M.GR_ZONE.width = M.POLE.bg.width*M.POLE.bg.xScale
M.GR_ZONE.height = M.POLE.bg.height*M.POLE.bg.yScale
M.GR_ZONE.x = M.POLE.bg.x-M.POLE.bg.width*M.POLE.bg.xScale/2
M.GR_ZONE.y = M.POLE.bg.y-M.POLE.bg.height*M.POLE.bg.yScale/2
-- player
local _x = M.arr_pole[1][1][1].x
local _y = M.arr_pole[1][1][1].y
M.POLE.player = image(IMAGES..'ui/player.png')
M.POLE.player.x = _x
M.POLE.player.y = _y
M.POLE.player.width = M.size_xonix*1.5
M.POLE.player.height = M.size_xonix*1.5
M.POLE.player.state = 0
M.GR_ZONE:insert(M.POLE.player)
M.POLE.player:toFront()
end
-- get all free cells
- land
M.get_free = function(_i,_j,_start)
local _par = pairs
local _fill = M.filling_arr
local _arr = M.arr_pole
local _index = {
{-1,-1}, {0,-1}, {1,-1},
{-1, 0}, {1, 0},
{-1, 1}, {0, 1}, {1, 1},
}
-- mark as verified
_arr[_i][_j].is_check = true
_fill[#_fill+1] = {_i,_j}
for k,v in _par(_index) do
local i = _i+v[1]
local j = _j+v[2]
if i>1 and i<M.delta_height
and j>1 and j<M.delta_width then
if not _arr[i][j].is_check then
if _arr[i][j].type==2 then
M.get_free(i,j)
end
end
end
end
if _start then
return _fill
end
end
-- fill(capture)
M.filling = function()
-- init
local par = pairs
local tb_rem = table.remove
local arr = M.arr_pole
M.island_arr = {}
-- check indicator
for i,ii in par(arr) do
for j,jj in par(ii) do
if jj.type==2 then
jj.is_check = false
end
end
end
-- we divide land into islands
for i,ii in par(arr) do
for j,jj in par(ii) do
if jj.type==2 and not jj.is_check then
M.filling_arr = {}
M.island_arr[#M.island_arr+1] = M.get_free(i,j,true)
end
end
end
- find a larger island and delete it
local sel_max = {dir = '', count = 1}
for k,v in par(M.island_arr) do
if #v>=sel_max.count then
sel_max.dir = k
sel_max.count = #v
end
end
if #M.island_arr>0 then
tb_rem(M.island_arr,sel_max.dir)
end
-- fill
for k,v in par(M.island_arr) do
for kk,vv in par(v) do
local obj = arr[vv[1]][vv[2]]
obj.type = 0
obj[1].isVisible = false
end
end
-- turning the edges into water
for k,v in par(M.bread_crumbs) do
local arr = arr[v.arr_y][v.arr_x]
arr.type = 0
arr[1].isVisible = false
end
M.clear_history()
end

Why is my lua table object empty?

I create a lua table object called Map in my Map module, and this function creates a new instance:
function Map:new (o)
o = o or {
centers = {},
corners = {},
edges = {}
}
setmetatable(o, self)
self.__index = self
return o
end
and in my island module I put in this code in the first few lines:
local map = require (*map module location*)
Island = map:new ()
and when I print the number of centers, corners, and tables, they all come out to 0.
I have separate modules for Corner:new (), Center:new (), and Edge:new ()
Why does the length of centers, corners, and edges output as 0?
Edit:
This is what I input into the centers table for example(corners and edges is similar)
function pointToKey(point)
return point.x.."_"..point.y
end
function Map:generateCenters(centers)
local N = math.sqrt(self.SIZE)
for xx = 1, N do
for yy = 1, N do
local cntr = Center:new()
cntr.point = {x = 0.5+xx - 1, y = 0.5+yy - 1}
centers[pointToKey(cntr.point)] = cntr
end
end
return centers
end
The size is always a perfect square
This seems to be a problem with variable scope. Firstly, in instantiating a new Map, the o that is returned should be local:
function Map:new (o)
local o = o or { -- this should be local
centers = {},
corners = {},
edges = {}
}
setmetatable(o, self)
self.__index = self
return o
end
When you pass a pointer to a table to Map:generateCenters(), there is no need to return that pointer. The centers have been added to that table:
function Map:generateCenters(centers)
local N = math.sqrt(self.SIZE)
for xx = 1, N do
for yy = 1, N do
local cntr = Center:new()
cntr.point = {x = 0.5+xx - 1, y = 0.5+yy - 1}
centers[pointToKey(cntr.point)] = cntr -- HERE you're adding to the table passed as an argument
end
end
-- return centers --> NO NEED TO RETURN THIS
end
Lastly, you would do:
local map = require( "map" )
local island = map:new()
map:generateCenters( island.centers )
You are saying, "Put the centers into the table pointed to by the table value corresponding to the key called centers in the table called island".
Lastly, note that
local t = island.centers
print( #t )
will still not output the number of elements in the table centers because there are gaps keys (i.e. they don't go {0,1,2,3,4,..} but rather whatever string the pointToKey() function returns ). To count the elements in centers, you could do:
local count = 0
for k,v in pairs( island.centers ) do
count = count + 1
end
print( count )

problems with Lua match to find a pattern

I'm struggling with this problem:
Given 2 strings:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
I would like to produce the following information:
If they match (these two above should match, s2 follows a pattern described in s1).
A table holding the values of s2 in with the corresponding name in s1. In this case we would have: { bar = "lua", rab = "rocks" }
I think this algorithm solves it, but I can't figure how to implement it (probably with gmatch):
store the placeholders : indexes as KEYS of a table, and the respective VALUES being the name of these placeholders.
Example with s1:
local aux1 = { "6" = "bar", "15" = "rab" }
With the keys of aux1 fetched as indexes, extract the values of s2
into another table:
local aux2 = {"6" = "lua", "15" = "rocks"}
Finally merge them two into one table (this one is easy :P)
{ bar = "lua", rab = "rocks" }
Something like this maybe:
function comp(a,b)
local t = {}
local i, len_a = 0
for w in (a..'/'):gmatch('(.-)/') do
i = i + 1
if w:sub(1,1) == ':' then
t[ -i ] = w:sub(2)
else
t[ i ] = w
end
end
len_a = i
i = 0
local ans = {}
for w in (b..'/'):gmatch('(.-)/') do
i = i + 1
if t[ i ] and t[ i ] ~= w then
return {}
elseif t[ -i ] then
ans[ t[ -i ] ] = w
end
end
if len_a ~= i then return {} end
return ans
end
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
for k,v in pairs(comp(s1,s2)) do print(k,v) end
Another solution could be:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
pattern = "/([^/]+)"
function getStrngTable(_strng,_pattern)
local t = {}
for val in string.gmatch(_strng,_pattern) do
table.insert(t,val)
end
return t
end
local r = {}
t1 = getStrngTable(s1,pattern)
t2 = getStrngTable(s2,pattern)
for k = 1,#t1 do
if (t1[k] == t2[k]) then
r[t1[k + 1]:match(":(.+)")] = t2[k + 1]
end
end
The Table r will have the required result
The solution below, which is some what cleaner, will also give the same result:
s1 = '/foo/:bar/oof/:rab'
s2 = '/foo/lua/oof/rocks'
pattern = "/:?([^/]+)"
function getStrng(_strng,_pattern)
local t = {}
for val in string.gmatch(_strng,_pattern) do
table.insert(t,val)
end
return t
end
local r = {}
t1 = getStrng(s1,pattern)
t2 = getStrng(s2,pattern)
for k = 1,#t1 do
if (t1[k] == t2[k]) then
r[t1[k + 1]] = t2[k + 1]
end
end

Resources