Drawing a Matrix - lua

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

Related

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.

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

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.

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.

box 2d concepts :Gideros (LUA)

In below mentioned code, what exactly is the difference between sprite, body and ground? Why are there no parameters passes to createBody while declaring ground?
what is difference between edgeshape.new() and polygonshape.new()?
require "box2d"
b2.setScale(20)
-- this function creates a box sprite with 2 happy and sad children
local function createBoxSprite(sx, sy)
local happy = Bitmap.new(Texture.new("happy-box.png", true))
happy:setAnchorPoint(0.5, 0.5)
local sad = Bitmap.new(Texture.new("sad-box.png", true))
sad:setAnchorPoint(0.5, 0.5)
local sprite = Sprite.new()
sprite:addChild(happy)
sprite:addChild(sad)
sprite:setScale(sx, sy)
return sprite
end
-- this table holds the dynamic bodies and their sprites
local actors = {}
-- create world
local world = b2.World.new(0, 9.8)
-- create a ground body and attach an edge shape
local ground = world:createBody({})
local shape = b2.EdgeShape.new(-200,480,520,480)
ground:createFixture({shape = shape, density = 0})
-- every 3 seconds, we create a random box
local function onTimer()
local sx = math.random(70, 100) / 100
local sy = math.random(70, 100) / 100
local body = world:createBody{type = b2.DYNAMIC_BODY, position = {x = math.random(0, 320), y = -35}}
local shape = b2.PolygonShape.new()
-- box images are 70x70 pixels. we create bodies 1 pixel smaller than that.
shape:setAsBox(34.5 * sx, 34.5 * sy)
body:createFixture{shape = shape, density = 1, restitution = 0.1, friction = 0.3}
local sprite = createBoxSprite(sx, sy)
stage:addChild(sprite)
actors[body] = sprite
end
Sprite is an empty object to group other Sprite inherited objects as in your case happy and sad Bitmaps.
Body is a representation of the physical body in box2d, it does not have any visual representation, only numbers as dimensions (width and height), position, rotation and different forces. Usually inside ENTER_FRAME event you take this values as position and rotation and apply them to the Sprite inherited object, so it would move exactly like the body in box2d simulated world.
Ground is an empty dummy box2d object. If you don't provide parameters to createBody it means the defaults are used, which basically means you don't care what values it has. The most common example of such body usage is for joints.
To create a joint you usually need two bodies, but what if you would to attach body through joint to some random position in the air, or dynamic position as mouse cursor, well you can do it by creating dummy physics object
Here is an example: http://appcodingeasy.com/Gideros-Mobile/Dragging-Box2d-object-in-Gideros-Mobile
In case of your example, then this body is used for EdgeShape which is usually an arbitrary shape used for creating world boundaries or in this case simply ground, so the dynamic object won't fall outside the screen

A lua script on roblox that moves a model upwards?

I have a model called door
Inside I have a BoolValue named Open
I have a model called Top that has all of the door blocks named Work Mabey Comeon and Proboblynot
And I have Block that when touched is supposed to make Top move up
Directly inside door I have this script
door = script.Parent
open = door.Open
Top = door.Top
opener = 18
speed = 100
steps = speed
startl = Top.CFrame
function MoveDoorToCFrame(cfrm,dr)
dr.Work.CFrame = cfrm
dr.Mabey.CFrame = dr.Work.CFrame * CFrame.new(0,-7.2,0)
dr.Comeon.CFrame = dr.Work.CFrame * CFrame.new(0,10.8,0)
dr.Problynot.CFrame = dr.Work.CFrame * CFrame.new(0,10.8,0)
end
function Update()
if speed/steps < 0.5 then
calc = 1-math.cos(math.rad((-90/speed)*steps*2))
else
calc = 1+math.sin(math.rad((90/speed)*((speed/2)-steps)*2))
end
MoveDoorToCFrame(startl * CFrame.new(0,(calc/2)*opener,0),Top)
end
Update()
while true do
wait()
if not open.Value and steps < speed then
steps = steps + 1
Update()
elseif open.Value and steps > 0 then
steps = steps - 1
Update()
end
end
Inside the button that is supposed to activate on touch I have
script.Parent.Touched:connect(function()
script.Parent.Parent.Open.Value = not script.Parent.Parent.Open.Value
end)
script.Parent.Parent.Open.Changed:connect(Update)
Update()
If you know how to fix this it would be gladly appreciated.
Update November 2015:
Using PrimaryPart
Since writing this post, ROBLOX has changed a lot in regards to the API. To move a model like requested, you should set the PrimaryPart property of the model to a central part inside the model. This will act as the origin for the model's movements.
You can then use model:SetPrimaryPartCFrame(cframe) to set the CFrame of the model. You can also retrieve this property by using model:GetPrimaryPartCFrame(), although I believe that is just a shortcut method for model.PrimaryPart.CFrame.
In code, it would look like this:
-- Set PrimaryPart:
MODEL.PrimaryPart = MODEL.SomeCentralPart
...
-- CFrame movement:
local movement = CFrame.new(0, 10, 0)
-- Move the model:
MODEL:SetPrimaryPartCFrame(MODEL:GetPrimaryPartCFrame() * movement)
Option A: Use Model's methods
I think you are making this much more difficult than it needs to be. Whenever you run into an issue like this, be sure to check the current APIs provided. The ROBLOX Model object contains a nifty method called 'TranslateBy' which takes a Vector3 argument to translate the model.
Using MODEL:TranslateBy(Vector3) is similar to moving a model via CFrame, since it ignores collisions.
Another alternative is MODEL:MoveTo(Vector3) which moves a whole model to the given Vector3 world position. The downside to this is that it does collide.
One way to get the same MoveTo effect but without collisions can be done with the TranslateBy method:
MODEL:TranslateBy(Vector3Position - MODEL:GetModelCFrame().p)
Option B: Write a custom function to manipulate the model's CFrame
Another alternative would be to manipulate the whole model's CFrame entirely. To do this, you can write a clever function that will move a whole model relative to an 'origin' point. This is similar to moving shapes on a grid given their points and an origin, except in three dimensions. Using ROBLOX's built-in functions, this is much easier though.
A good way to do this would be to write a function that lets you actually assign a CFrame value to a whole model. Another way would be to allow a translation via CFrame too.
Here's an example:
function ModelCFrameAPI(model)
local parts = {} -- Hold all BasePart objects
local cf = {} -- API for CFrame manipulation
do
-- Recurse to get all parts:
local function Scan(parent)
for k,v in pairs(parent:GetChildren()) do
if (v:IsA("BasePart")) then
table.insert(parts, v)
end
Scan(v)
end
end
Scan(model)
end
-- Set the model's CFrame
-- NOTE: 'GetModelCFrame()' will return the model's CFrame
-- based on the given PrimaryPart. If no PrimaryPart is provided
-- (which by default is true), ROBLOX will try to determine
-- the center CFrame of the model and return that.
function cf:SetCFrame(cf)
local originInverse = model:GetModelCFrame():inverse()
for _,v in pairs(parts) do
v.CFrame = (cf * (originInverse * v.CFrame))
end
end
-- Translate the model's CFrame
function cf:TranslateCFrame(deltaCf)
local cf = (model:GetModelCFrame() * deltaCf)
self:SetCFrame(cf)
end
return cf
end
-- Usage:
local myModel = game.Workspace.SOME_MODEL
local myModelCF = ModelCFrameAPI(myModel)
-- Move to 10,10,10 and rotate Y-axis by 180 degrees:
myModelCF:SetCFrame(CFrame.new(10, 10, 10) * CFrame.Angles(0, math.pi, 0))
-- Translate by 30,0,-10 and rotate Y-axis by 90 degrees
myModelCF:TranslateCFrame(CFrame.new(30, 0, -10) * CFrame.Angles(0, math.pi/2, 0))
This might be hard.
You might want to look to free models for this one unless the people above get it to work.
I, however, do have a script to move a model:
game.Workspace.Model:MoveTo(Vector3.new(0,0,0))
Your code indeed needs fixing.
You should NOT use a never-ending loop to make your stuff work (unless that is the only way).
You should rather base actions on events.
Consider to use this:
Structure:
Door [Model]
DoorScript [Script]
Button [Part]
DoorOpen [BoolValue]
Top [Model]
Mabey [Part]
Comeon [Part]
Problynot [Part]
DoorScript:
local Model = script.Parent
local Door = Model.Top
local Button = Model.Button
local DoorOpen = Model.DoorOpen
local Offset = 0
local ToOffset = 100
local Direction = 1
local StepLength = 0.1
local Moving = false
function StartMoving()
if Moving then return end
Moving = true
while (DoorOpen.Value and Offset ~= ToOffset) or (not DoorOpen.Value and Offset ~= 0) do
local Change = Offset
Offset = math.max(0,math.min(ToOffset,Offset + StepLength * (DoorOpen.Value and 1 or -1)))
Change = Offset - Change
Top:TranslateBy(Vector3.new(0,Change,0))
wait()
end
Moving = false
end
StartMoving()
DoorOpen.Changed:connect(StartMoving)
local Debounce = false
Button.Touched:connect(function()
if Debounce then return end
Debounce = true
DoorOpen.Value = not DoorOpen.Value
wait(4)
Debounce = false
end)
You might want to adjust the speed tho.
This can be used to move models, try adding something like this into your code. It's more dynamic.
a = Workspace.Model
for i=0.1,40 do
for i,v in pairs(a:getChildren()) do
if v:IsA("Part") then
v.CFrame = CFrame.new(v.CFrame + Vector3.new(0,0.1,0))
else print("Not a part")
end
end
end

Resources