Lua: When does it call functions? - lua

Using the Love framework by the way.
Ok, so I'm looking to create a random map generation. I obviously only want it drawn once, so I tried to set up a very basic structure. Which isn't working and I can't figure out why.
function love.load()
testVar = 1
end
function love.draw()
if testVar == 1 then
testFunction()
love.graphics.print("Update", 20, 200)
end
love.graphics.print(testVar, 100, 100)
end
function testFunction()
love.graphics.print("Success", 20, 300)
testVar = 0
end
What that is doing is only printing "0", thanks to the command to print testVar up in the draw function. So it seems that it's updating the testVar value without actually running testFunction. Is this something to do with Lua?
For those unfamiliar with Love, love.draw is called every frame, love.load only initially.

This code works as expected. It's just love.draw is called every frame (multiple times per second), so the output from the first frame is quickly overwritten by next frames. Usually you use love.update when you need to make changes to your state (for example, based on user input) and love.draw to draw that state on the screen (every frame).

Related

Roblox Lua Humanoid:Move

Pardon the noob question, I am new to Roblox LUA.
I am trying to make the humanoid move forwards and backwards, (as when an officer, guards a perimeter by moving to and fro) but there may be something wrong with this script, because it only moves forwards.
local toggle = true
local RunService = game:GetService("RunService")
local humanoid = game.Players.LocalPlayer.Character:WaitForChild("Humanoid")
RunService:BindToRenderStep("Move", Enum.RenderPriority.Character.Value + 1, function()
while toggle do
humanoid:Move(Vector3.new(0, 0, -1), true)
wait(1)
humanoid:Move(Vector3.new(0, 0, 1), true)
wait(1)
end
end)
And then when I toggle = false, it does not stop.
I can't use keypress as it defeats the purpose of what I intend to do.
Thank you for any assistance.
The way this code is structured does not make much sense. You're creating a loop inside a function that is triggered every frame. Every single frame, you create a new loop... After just 10 seconds you might have 300 loops trying to move the humanoid at the same time.
Because the loops constantly overwrite each other, the last one to run takes precedence... Which is likely why it's only towards one direction.
I presume you want to make the character move towards (0,0,-1) for a second, then towards (0,0,1) for another second and then, if toggle is enabled, run again.
What you should be doing instead is not creating a loop inside the BindToRenderStep, but setting the movement every frame according to where the character should be moving to, and running that loop outside, once:
local toggle = true
local RunService = game:GetService("RunService")
local humanoid = game.Players.LocalPlayer.Character:WaitForChild("Humanoid")
movement = nil
RunService:BindToRenderStep("Move", Enum.RenderPriority.Character.Value + 1, function()
if movement then
humanoid:Move(movement, true)
end
end)
while toggle do
movement = Vector3.new(0, 0, -1)
task.wait(1)
movement = Vector3.new(0, 0, 1)
task.wait(1)
end
movement = nil
Mind that this code has some quirks:
Nothing will be able to run after the while, so nothing will realistically set the toggle off if placed after while toggle do. It depends on where you want to switch the toggle how you'd handle this.
Depending on where you've placed this, it might error. If this is in StarterPlayerScripts you should use the CharacterAdded event to wait until the character exists, and then handle setting the humanoid again when respawning (because the humanoid will not be the same one if the character respawns). If this is in StarterCharacterScripts, there is no need to access the LocalPlayer, you can just do script.Parent:WaitForChild("Humanoid") (though it also comes down to personal preference).
You've made the movement relative to the camera with that true argument to :Move() https://create.roblox.com/docs/reference/engine/classes/Humanoid#Move. The player will still be able to move the camera to change how the movement direction.

Trying to Reset Game in Lua

Very new to game development and Lua in general here. I'm making a platformer, and I want to be able to restart the game once a character collides with a certain object, and show a title before that. I also want to reset the character's position back to 0,0 once that happens, but I don't know how.
I made a global variable called WIN that's set to true if the character collides with the object, which works, but then going into my love.draw() function, I have this:
function love.draw()
-- begin virtual resolution drawing
push:apply('start')
-- clear screen using Mario background blue
love.graphics.clear(108/255, 140/255, 255/255, 255/255)
-- renders our map object onto the screen
love.graphics.translate(math.floor(-map.camX + 0.5), math.floor(-map.camY + 0.5))
map:render()
if WIN == true then
love.graphics.printf('NEXT LEVEL', 0, 30, VIRTUAL_WIDTH, 'center')
love.graphics.printf('Continue to Next Level', 0, 45, VIRTUAL_WIDTH, 'center')
love.load()
end
-- end virtual resolution
push:apply('end')
end
When I actually collide with the object, I get the following error:
Error
push.lua:48: love.window.setMode cannot be called while a Canvas is active in love.graphics.
Traceback
[C]: in function 'windowUpdateMode'
push.lua:48: in function 'setupScreen'
main.lua:43: in function 'load'
main.lua:116: in function 'draw'
[C]: in function 'xpcall'
The error lines are in my love.load() function, which is as follows:
function love.load()
-- sets up a different, better-looking retro font as our default
love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8))
-- sets up virtual screen resolution for an authentic retro feel
push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, {
fullscreen = false,
resizable = true
})
love.window.setTitle('Super Mario 50')
love.keyboard.keysPressed = {}
love.keyboard.keysReleased = {}
end
I'm guessing the issue is that I can't set up screen again after already doing that once, but I don't know how to fix this and create a fresh start. Any help is appreciated!
love.load() runs once. That one time is when your game starts up. Don't call love.load() (or love.update()) within love.draw() or vice versa. The Love2D game engine calls these functions, so you don't call them.
Update the start position within love.update() not love.draw(). love.draw() is only for drawing stuff. I believe it would be the View in a model-view-controller architecture.
To restart the position of your object, you would have to do something like
if WIN == true then
push.x = push.start.x
push.y = push.start.y
map.camX = map.start.camX
map.camY = map.start.camY
end
Or something along those lines, it's hard to know without seeing your variables. Your love.draw() will then draw the things at the coordinates that they are at.
To reset/restart the Game without reloading the executable use:
love.event.quit('restart')
But before doing this make a dark screen before with red dropping letters: GAME OVER

How to change the velocity in each frame in Love2D?

I'm trying to code a Pong game on Lua using the framework Love2D with some extra features. Among others, I want curves to occur. In order to do so, I'm trying to implement the trajectory of horizontally launched projectiles. I have a ball table with a position attribute for x (ball.x) and another for y (ball.y). I, also, have a an attribute for the x velocity (ball.dx) and another for the y velocity (ball.dy). Finally, I have an acceleration variable (gravity)
In my game, if the paddle moves and the ball hits it, the ball should follow and horizontal curve. In order to create my curves, I want to change the y-axis velocity on each frame in order to make my ball move in an arc across the screen. The main issue that I have is that I don't know how to change this velocity in each frame in order to create the expected arc. The most recent attempt that I made was to create a while loop like the following code. However, it creates an infinite loop. Can someone enlighten me please?
Clarification:
-Player.x and player.y are the player coordinate
-Player2.x and Player2.y are the opponent coordinate
-This piece of code is inside another if statement detecting a collision. It is inside the love.update(dt) function.
Thank you so much!
if love.keyboard.isDown("up") and distanceBetween(ball.x,ball.y,player.x,player.y)>30 then
ball.dy=0
while CollisionDetector(ball.x,player2.x,ball.y,player2.y, player2.width,player2.height,ball.size)==false or ball.x>0 or ball.y-20>0 or ball.y+20<love.graphics.getHeight() do
ball.dy=ball.dy+gravity*dt
ball.y=ball.y+ball.dy
end
end
I believe the snippet you've provided is a part of love.update function. (At least it should be.) That function is supposed to perform a single step of the game at a time and return. It is not supposed to handle the whole flight in a single call.
In your particular case, while loop expects the ball to move, but that is not something that will happen until the loop and encompassing function end.
In general, to simulate ongoing processes you'll have to store the information about such process. For example, if you want gravity be dependent on the paddle movement direction, then you'll have to modify the corresponding variable and store it between the call.
In your code, there are multiple other design flaws that make it do not what you think it should do. With some functions and code parts left to be implemented by yourself, the code outline would look as follows:
function collides(ball,player)
return (ball.x,player.x,ball.y,player.y, player.width,player.height,ball.size)
end
function love.update(dt)
handle_paddle_movements()
--handle the ball-paddle interaction
if collides(ball,player1) or collides(ball,player2) then
ball.dx=-ball.dx
if love.keyboard.isDown("up") then
--store the info you need to know about the interaction
ball.d2y = -gravity
else if love.keyboard.isDown("down")
ball.d2y = gravity
else
ball.d2y = 0
end
end
handle_the_wall_collision()
--ball movement code should be separate from collision handling
--concrete equations can be whatever you want
ball.x = ball.x+ball.dx*dt
ball.dy = ball.dy + ball.d2y * dt
ball.y = ball.y + ball.dy * dt
end

Changing a moving objects direction of travel in corona

I'm new to Corona and looking for a little help manipulating moving objects:
Basically I want a set up where when I can click on a moving object, a dialog box will pop up giving me the option to change the speed of the object and the vector of travel. I'm pretty sure I can figure out the event handling and the dialog but I'm stuck on simply changing the direction of travel to the new vector
in a simple example, I have a rect moving up the screen as follows:
obj1 = display.newRect(500, 800, 10, 40)
transition.to(obj1,{x=500, y = 100, time = 40000})
I know I can change the speed by adjusting the time, but if I use
obj1:rotate(30)
to turn the object 30 degrees, how do I make it travel in the new direction?
Should I be using physics - linear impulse for example, instead of transitions?
Apologies if this is a stupid question but I have searched without success for a solution.
This sounds like what you are trying to do. You would have to modify bits to fit your code but this is a working example. So if you copy it to a new main.lua file and run it you can see how it works. (Click to rotate obj).
local obj = display.newRect(50,50, 10, 40)
local SPEED = 1
local function move(event)
obj.x = obj.x + math.cos(math.rad(obj.rotation)) * SPEED
obj.y = obj.y + math.sin(math.rad(obj.rotation)) * SPEED
end
local function rotate(event)
obj.rotation = obj.rotation + 45
end
Runtime:addEventListener("enterFrame", move)
Runtime:addEventListener("tap", rotate)
Basically I used the "enterFrame" event to 'move' the rectangle, by recalculating the x and y of the objects location using its rotation (which is easy enough to modify) every frame.

Corona/Box2D detect collision with non-moving static objects

For posting reasons here's a simple version of what I'm trying to do.
On the screen I have a simple circle object that is static and doesn't move. The user can then drag and drop a straight line. If the line passes through that circle I'm hoping for the collision event to be triggered.
It appears that unless one of the objects are moving the collision is never detected. Can I detect the collision when the line is drawn?
Collision Event
function onHit(e)
print("hit");
end
Runtime:addEventListener("collision", onHit)
Touch Event
local startX = 0;
local startY = 0;
local endX = 0;
local endY = 0;
function onTouch(e)
if(e.phase == "began") then
startX = e.x
startY = e.y
elseif(e.phase == "moved") then
endX = e.x
endY = e.y
elseif(e.phase == "ended") then
local line = display.newLine(startX, startY, endX, endY)
line:setColor(100, 100, 100)
line.width = 2
physics.addBody(line, "static", { })
end
end
Runtime:addEventListener("touch", onTouch)
Create circle
local c = display.newCircle(50, 50, 24)
physics.addBody(c, "static", { radius = 24 })
This page from the Corona SDK docs describes the bodyType property about halfway down the page. When describing "static" bodies, it says (my emphasis):
static bodies don't move, and don't interact with each other;
examples of static objects would include the ground, or the walls of a
pinball machine.
That means that one of the objects has to be something other than static.
Here's an idea, although I haven't tried it myself: (See update below.) Make the line dynamic when you first create it. Set it to static a few milliseconds later using a timer.performWithDelay function. If a collision event occurs in the meantime, you will know that you've got an overlap, and can set the bodyType back to static immediately. If you don't get a collision event, the bodyType will still be dynamic in the delayed routine, and you'll know you didn't have an overlap. In this case, you'll still need to set the line to static in the delayed routine.
UPDATE: Tested this, using your code as the starting point
I changed the collision event to always set both objects' bodyType to static:
function onHit(e)
print("hit")
e.object1.bodyType = "static"
e.object2.bodyType = "static"
end
Then I changed the addBody call for the line to add it as a dynamic body, with new code to setup a timer.PerformWithDelay function to check after a short time:
physics.addBody(line, "dynamic", { })
timer.performWithDelay(10,
function()
if line.bodyType == "dynamic" then
print ("NO OVERLAP")
line.bodyType = "static"
end
end)
The results were, unfortunately, mixed. It works most of the time, perhaps 95%, but fails occasionally when drawing a line that starts outside the circle and ends inside, which should be an overlap, but is sometimes reported as no overlap. I wasn't able to figure out why this was happening. I'm posting this anyway, hoping that it gets you going, and also thinking that someone may be able to figure out the inconsistent behavior and educate us both.
Failing that, you could add an additional check for the "no overlap" case to check if either endpoint of the line is closer than the radius of the circle away from the center. That would be make things work, but I suppose it misses the whole point of letting the physics engine to the work.
Anyway, good luck!
Perform a raycast when you release your mouse press. This way you can keep both objects static, and know that they intersect via the raycast callback.
(I know that this is an old post, but it was the first hit on my Google search and is incorrect afaic)
Set the body type as kinematic, set it as a sensor, and update its position to the entity it's tied to every frame. Unlike static, kinematic can interact with static.

Resources