How does parameter for functions work in lua? - lua

So I got this chunk of code that for some reason is giving me a null exception on LOVE
function enemies_controller:spawnEnemy(x, y, rad)
enemy = {}
enemy.x = x
enemy.y = y
enemy.rad = rad
enemy.speedMulti = 1
table.insert(enemies_controller.enemies, enemy)
end
And the function calling it like this:
enemies_controller.spawnEnemy(100, 100, 50)
The thing is that is giving me a null exception on the enemy.rad when i draw, cause enemy.x takes the second function parameter and enemy.y the third, and none takes the first one... So i am unsure of what is happening

It's because using : creates a "self" parameter. (This is a typing shortcut because lots of people make functions with self parameters. It doesn't do anything special.)
function enemies_controller:spawnEnemy(x, y, rad)
is the same as
function enemies_controller.spawnEnemy(self, x, y, rad)
or in other words:
enemies_controller.spawnEnemy = function(self, x, y, rad)
Notice the difference between . and :.
A similar thing when you call it:
enemies_controller:spawnEnemy(100, 100, 50)
is the same as:
enemies_controller.spawnEnemy(enemies_controller, 100, 100, 50)
and the first one is probably how you're meant to call the function - there's no other reason they'd define it with :.
If you call
enemies_controller.spawnEnemy(100, 100, 50)
then you pass 100 as self, 100 as x and 50 as y and nil as rad

Related

I don't Understand "How Lua save data on the same variable when we Iterate over it"

I know Lua remember value in the same field/function with the Same defined Variable. but I still lack the concept behind it. Let's say I'see Int Variable = 2 which can be modify if we iterate over it Int variable = 8
Same logic I was applying on the Code. I got the results, but I didn't understand the concept behind it.
You Can see in my Code I was saving some value in Var x and saveX
and I was doing saveX = x from my logic The value of x should be saved inside saveX instead it was saving the loop value as I'm applying over it. Why is that?
-- Graphical Representation of table1
--[[ { { {},{},{},{} },
{ {},{},{},{} },
{ {},{},{},{} },
{ {},{},{},{} } } ]]
_Gtable1 = {}
for y = 1, 4 do
table.insert(_Gtable1, {})
for x = 1, 4 do
table.insert(_Gtable1[y], {
x = (x - 1) * 10, --Coordinate value of X
y = (y - 1) * 10, --Coordinate value of Y
-- what if i want to save Coordinate value on another variable How should i do?
saveX = x, -- Saving Loop X
saveY = y, -- Saving Loo Y
t = "Check" -- to Debug
})
end
end
ti = _Gtable1
for y = 1, 4 do
for x = 1, 4 do
tim = ti[y][x]
-- print(tim.saveX) -- output 1, 2, 3, 4
print(tim.x) -- output 0, 10, 20, 30
end
end
I hope I understood the question:
Let's simplify the example:
local t = {
x = 10,
y = x,
}
You assume t.y is 10, because you declared it a line before that? But it is nil here, because x = 10, is no variable. It's part of the table constructor and can not be referenced in the same constructor. Check out https://www.lua.org/pil/3.6.html for more details.
After the constructor has finished, you can access that as t.x (not x!).
Now for your example, you need to move (x - 1) * 10 outside the constructor, e.g.: local tempX = (x - 1) * 10. Then use tempX inside the constructor. Or you can set your values individually, e.g. _Gtable1[y][x].saveX = _Gtable1[y][x].x. I would prefer the former.

Lua says that these variables are numbers but I can't use them to make a circle in Love2d

edit: terrible mistake, just forgot to provide radius lol. Thanks for helping :)
I am so confused by this happening. I'm fairly certain all parts of my code work but for some reason, the x and y co-ordinates are not being printed by Love2D, despite showing them on screen and using type() to show that they're numbers.
function love.load()
window = {}
window.x, window.y = love.graphics.getDimensions()
enemies = {}
end
function newenemy()
x = math.random(0, window.x)
y = math.random(0, window.y)
table.insert(enemies, {x = x, y = y, speed = 5})
end
function love.update(dt)
newenemy()
for i=#enemies, 1, -1 do
local angle = math.atan((love.mouse.getY()-enemies[i]['y'])/(love.mouse.getX()-enemies[i]['x']))
if love.mouse.getX() < enemies[i]['x'] then angle = angle + math.pi end
enemies[i]['x'] = enemies[i]['x'] + math.cos(angle)*enemies[i]['speed']
enemies[i]['y'] = enemies[i]['y'] + math.sin(angle)*enemies[i]['speed']
end
end
function love.draw()
for i=1, #enemies do
love.graphics.print('('..enemies[i]['x']..','..enemies[i]['y']..')')
love.graphics.circle('fill', enemies[i]['x'], enemies[i]['y'])
end
end
To show the actual numbers, I've been commenting out the line to draw the circle and watching the numbers change as I move my cursor across the screen. I truly don't know what is going on. This code is from a much larger (~400 lines) script that I'm writing for and I thought there was some error with the variables but it still seems tp occur in 30 lines of code.
Error
main.lua:27: bad argument #4 to 'circle' (number expected, got no value)
Traceback
[C]: in function 'circle'
main.lua:27: in function 'draw'
[C]: in function 'xpcall'
Note the error
main.lua:27: bad argument #4
speaks of the fourth argument, wherein you are only providing three.
The fourth argument to love.graphics.circle is the radius of the circle.
love.graphics.circle('fill', enemies[i]['x'], enemies[i]['y'], 10)

Error main.lua:45: attempt to call global 'distanceFormula' (a nil value)

I keep getting this error, no matter what:
function love.load() -- love.load starts any command inthe beginning
--number = 0 (old code)
button = {}
button.x = 200
button.y = 200
button.size = 50
button.score = 0
button.time = 0
newFont = love.graphics.newFont(40)
end
end
function love.update(dt) -- changes code in delta time: every frame: love runs in 60 frames per second
--number = number + 1 (old code)
end
function love.draw() -- does anything on the screen
end
function love.draw()
--love.graphics.setColor(0, 0, 255, 5)
--love.graphics.rectangle("fill", 200, 400, 200, 100) --love.graphics.rectangle(mode, x, y, width, height) Y increases downwards;
-- width increases to the right and the height increases downward assuming positive numbers
love.graphics.setColor(255,0,0,5)
--love.graphics.print(number) (old code)
--love.graphics.circle("fill", 150, 350, 100)
--love.graphics.circle(mode, x, y, radius, segments)
love.graphics.circle("fill", button.x, button.y, button.size)
love.graphics.setFont(newFont)
love.graphics.setColor(255, 255, 255, 1)
love.graphics.print(button.score)
end
function love.mousepressed( x, y, b, isTouch)
if b == 1 then
-- use the distance formula to measure the distance/n!
if distanceFormula(button.x, button.y, love.mouse.getX(), love.mouse.getY()) < button.size then
--checks whether click is in circle.
score = score + 1
end
end
function distanceBetween(x1,y1,x2,y2)
return math.sqrt((y2-y1)^2 + (x2-x1)^2)
end
Can someone help me?
The error goes like this:
Error main.lua:45: attempt to call global 'distanceFormula' (a nil value)
Traceback
main.lua:45: in function <main.lua:42>
[C]: in function 'xpcall
I'm very sure that it probably has to do with the function place or something. Can someone help me?
Most importantly can someone please help me with preventing future errors like this??? Thank you.
It looks like you don't have a function called distanceFormula. Also I don't know why you are declaring your distanceBetween function inside of love.mousepressed. distanceFormula is "a nil value" because it doesn't exist. The error is referring to line 45 in your code
if distanceFormula(button.x, button.y, love.mouse.getX(), love.mouse.getY()) < button.size then
I changed your call to distanceFormula to be a call to distanceBetween and moved distance between to it's own function. I also had to move a few of your end around and got rid of your extra love.draw()
function love.load()
--number = 0 (old code)
button = {}
button.x = 200
button.y = 200
button.size = 50
button.score = 0
button.time = 0
newFont = love.graphics.newFont(40)
end
function love.update(dt) -- changes code in delta time: every frame: love runs in 60 frames per second
--number = number + 1 (old code)
end
function love.draw()
--love.graphics.setColor(0, 0, 255, 5)
--love.graphics.rectangle("fill", 200, 400, 200, 100) --love.graphics.rectangle(mode, x, y, width, height) Y increases downwards;
-- width increases to the right and the height increases downward assuming positive numbers
love.graphics.setColor(255,0,0,5)
--love.graphics.print(number) (old code)
--love.graphics.circle("fill", 150, 350, 100)
--love.graphics.circle(mode, x, y, radius, segments)
love.graphics.circle("fill", button.x, button.y, button.size)
love.graphics.setFont(newFont)
love.graphics.setColor(255, 255, 255, 1)
love.graphics.print(button.score)
end
function love.mousepressed(x, y, b, isTouch)
if b == 1 then
-- use the distance formula to measure the distance/n!
if distanceBetween(button.x, button.y, love.mouse.getX(), love.mouse.getY()) < button.size then
--checks whether click is in circle.
button.score = button.score + 1
end
end
end
function distanceBetween(x1,y1,x2,y2)
return math.sqrt((y2-y1)^2 + (x2-x1)^2)
end

What's the most efficient way to implement classes in Lua?

By efficient, I mean performance wise. If you have to access class members rapidly, like when drawing UI, what is the best way to index them?
My understanding is that table-based classes use less memory and are faster at creating instances, while closure-based classes have faster function calls and let you have private fields which are quickly indexed because they are stored as upvalues. What's the best implementation for a situation like the example class below?
-- Example of Table-based Class
local class = {}
class.x = 0
class.y = 0
class.w = 0
class.h = 0
-- Draw would be called for potentially dozens of instances many times per second
function class:Draw()
draw_rect(self.x, self.y, self.w, self.h)
end
-- Example of Closure-based class
local function class(_x, _y, _w, _h)
-- the new instance
local self = {
-- public fields
visible = false
}
-- private fields are locals
local x, y, w, h = _x, _y, _w, _h
function self.SetPos(_x, _y)
x = _x
y = _y
end
function self.GetPos()
return x, y
end
function self.GetVisible()
return self.visible
end
-- return the instance
return self
end
local obj = class(10, 20, 40, 80)
print(obj.GetPos()) --> 10, 20
obj.SetPos(50, 100)
print(obj.GetPos()) --> 50, 100
obj.x = 21
obj.y = 42
print(obj.GetPos()) --> 50, 100 (unchanged, private)
obj.visible = true
print(obj.GetVisible()) -- true (public)

How to use bounding box in Love2d?

I've been using some extremely bulky code to detect collision between simple objects, and I've heard about bounding boxes. I can't find any tutorials on how to use it, so I'm asking about how to use it. Here is how I detect collision:
function platform.collision()
if player.x + player.width / 2 <= platform.x + platform.width and
player.x + player.width / 2 >= platform.x and
player.y + player.height <= platform.y + platform.height and
player.y + player.height >= platform.y then
The MDN has a rather concise article on 2D collision detection. Being the MDN, the examples are in javascript, but are easily translated to, and applicable in, any language - including Lua.
Let's take a look:
Axis-Aligned Bounding Box
One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning no rotation. The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist.
Their example, translated to Lua:
local rect1 = { x = 5, y = 5, width = 50, height = 50 }
local rect2 = { x = 20, y = 10, width = 10, height = 10 }
if
rect1.x < rect2.x + rect2.width and
rect1.x + rect1.width > rect2.x and
rect1.y < rect2.y + rect2.height and
rect1.height + rect1.y > rect2.y
then
-- collision detected!
end
-- filling in the values =>
if
5 < 30 and
55 > 20 and
5 < 20 and
55 > 10
then
-- collision detected!
end
A live example, again in JavaScript, demonstrates this well.
Here's a quick (and imperfect) Love2D example you can throw into a main.lua and play around with.
local function rect (x, y, w, h, color)
return { x = x, y = y, width = w, height = h, color = color }
end
local function draw_rect (rect)
love.graphics.setColor(unpack(rect.color))
love.graphics.rectangle('fill', rect.x, rect.y,
rect.width, rect.height)
end
local function collides (one, two)
return (
one.x < two.x + two.width and
one.x + one.width > two.x and
one.y < two.y + two.height and
one.y + one.height > two.y
)
end
local kp = love.keyboard.isDown
local red = { 255, 0, 0, 255 }
local green = { 0, 255, 0, 255 }
local blue = { 0, 0, 255, 255 }
local dim1 = rect(5, 5, 50, 50, red)
local dim2 = rect(20, 10, 60, 40, green)
function love.update ()
if kp('up') then
dim2.y = dim2.y - 1
end
if kp('down') then
dim2.y = dim2.y + 1
end
if kp('left') then
dim2.x = dim2.x - 1
end
if kp('right') then
dim2.x = dim2.x + 1
end
dim2.color = collides(dim1, dim2) and green or blue
end
function love.draw ()
draw_rect(dim1)
draw_rect(dim2)
end
Oka explained it very well. This works for everything rectangular, not rotated and axis aligned. And you even already did it that way. This is great for buttons and the like!
But what I like doing is using (invisible) circles around objects and see if these collide. This works for everything where height is about the same as the width (which is the case for many sidescrolling platformers or top-down RPGs).
It's quite handy if you want to have the object centered at the current position. And it's especially helpful to simulate a finger on a touchscreen device, because a finger is quite a bit bigger than a mouse cursor. ;)
Here's an example on how to use this method. You can copy it as an actual game, it'll work.
--[[ Some initial default settings. ]]
function love.load()
settings = {
mouseHitbox = 5, -- A diameter around the mouse cursor.
-- For a finger (thouchscreen) this could be bigger!
}
objects = {
[1] = {
x = 250, -- Initial X position of object.
y = 200, -- Initial Y position of object.
hitbox = 100, -- A diameter around the CENTER of the object.
isHit = false -- A flag for when the object has been hit.
},
[2] = {
x = 400,
y = 250,
hitbox = 250,
isHit = false
}
}
end
--[[ This is the actual function to detect collision between two objects. ]]
function collisionDetected(x1,y1,x2,y2,d1,d2)
-- Uses the x and y coordinates of two different points along with a diameter around them.
-- As long as these two diameters collide/overlap, this function returns true!
-- If d1 and/or d2 is missing, use the a default diameter of 1 instead.
local d1 = d1 or 1
local d2 = d2 or 1
local delta_x = x2 - x1
local delta_y = y2 - y1
local delta_d = (d1 / 2) + (d2 / 2)
if ( delta_x^2 + delta_y^2 < delta_d^2 ) then
return true
end
end
--[[ Now, some LÖVE functions to give the collisionDetection() some context. ]]
function love.draw()
for i=1,#objects do -- Loop through all objects and draw them.
if ( objects[i].isHit ) then
love.graphics.setColor(255, 0, 0) -- If an object is hit, it will flash red for a frame.
objects[i].isHit = false
else
love.graphics.setColor(255, 255, 255)
end
love.graphics.circle("line", objects[i].x, objects[i].y, objects[i].hitbox/2)
end
end
-- You can use the following to check, if any object has been clicked on (or tapped on a touch screen device).
function love.mousepressed(x,y,button)
if ( button == 1 ) then
local i = objectIsHit(x,y) -- Check, if an object has been hit.
if ( i ) then
-- The object number 'i' has been hit. Do something with this information!
objects[i].isHit = true
end
end
end
function objectIsHit(x,y)
for i=1,#objects do -- Loop through all objects and see, if one of them has been hit.
if ( collisionDetected(x, y, objects[i].x, objects[i].y, settings.mouseHitbox, objects[i].hitbox) ) then
return i -- This object has been hit!
end
end
end
-- For the sake of completeness: You can use something like the following to check, if the objects themselves collide.
-- This would come in handy, if the objects would move around the screen and then bounce from each other, for example.
function love.update(dt)
if ( collisionDetected(objects[1].x, objects[1].y, objects[2].x, objects[2].y, objects[1].hitbox, objects[2].hitbox) ) then
-- The objects collided. Do something with this information!
end
end
As you can see, the collisionDetection() function is quite easy and intuitive to use.
Hopefully I could give you some more insight. And have fun with LÖVE 2D! :)

Resources