lua - How to perform transitions in sequence - animation

i'm trying to move an object along the points of a complex curved path with a constant velocity using transitions.
I have two tables to keep the coordinates of the points and another table with the respective time intervals for travelling each linear segment at the same speed (despite they have different lengths).
Assuming the firts and last values of the "timeTable" are 0, i tried with something similar to this:
local i = 1
local function Move()
transition.to(player, {time=timeTable[i+1], x=TableX[i+1], y=TableY[i+1]})
i=i+1
end
timer.performWithDelay( timeTable[i], Move, 0 )
It doesn't work although it no error is given.
Thanks in advance for your helpenter code here

May be this would work
local timeTable = {1, 3, 4, 1}
local TableX = {100, 400, 400, 500}
local TableY = {100, 100, 500, 500}
local i = 0
local function onCompleteMove()
i = i + 1
if timeTable[i] then
transition.to(player, {
time=timeTable[i],
x=TableX[i],
y=TableY[i],
onComplete=onCompleteMove
})
end
end
onCompleteMove() -- start moving to first point

Try
Tutorial: Moving objects along a path
Tutorial: Working with curved paths
Method for chain of transition for the same object
local function chainOfTransitions(object, params, ...)
if params then
function params.onComplete()
chainOfTransitions(object, unpack(arg))
end
transition.to(object, params)
end
end

Thanks to all of you!
I accomplished the goal by doing so:
local segmentTransition
local delta = 1
local function onCompleteMove()
i = i + delta
if timeTable[i] then
segmentTransition = transition.to(player2, {
time=timeTable[i],
x=tableX[i+delta],
y=tableY[i+delta],
onComplete=onCompleteMove
})
end
end
onCompleteMove() -- start moving

Related

Unable to cast to dictionary - Luau/Roblox Lua

I'm building a game in Roblox and I'm trying to make an NPC go towards the player using PathfindingService, and when the path doesn't work it wanders. However, I keep getting the error message "Unable to cast to Dictionary - Server - Script" and I can't seem to find the source of the issue.
I've checked my code for any tables or dictionaries that may be causing the problem, but I can't seem to find anything. Can anyone help me identify the issue and suggest a possible solution?
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local NPC = script.Parent
local TargetPlayer = nil
local Path = nil
local CurrentWaypointIndex = 1
local WaypointReachedDistance = 2
function findTargetPlayer()
local players = Players:GetPlayers()
if #players > 0 then
return players[1]
end
return nil
end
function findPathToTarget()
if not TargetPlayer then
return
end
local humanoid = TargetPlayer.Character and TargetPlayer.Character:FindFirstChild("Humanoid")
if not humanoid then
return
end
local startPosition = NPC.HumanoidRootPart.Position
local endPosition = humanoid.RootPart.Position
Path = PathfindingService:CreatePath(startPosition, endPosition)
Path:ComputeAsync()
if Path.Status == Enum.PathStatus.Success then
CurrentWaypointIndex = 1
else
NPC.Humanoid.WalkToPoint = NPC.HumanoidRootPart.Position + Vector3.new(math.random(-5,5), 0, math.random(-5,5))
end
end
function updateWaypoint()
if not Path or CurrentWaypointIndex > #Path.Waypoints then
return
end
local currentWaypoint = Path.Waypoints[CurrentWaypointIndex]
local distanceToWaypoint = (currentWaypoint - NPC.HumanoidRootPart.Position).Magnitude
if distanceToWaypoint <= WaypointReachedDistance then
CurrentWaypointIndex += 1
end
NPC.Humanoid:MoveTo(NPC.HumanoidRootPart.Position + Vector3.new(math.random(-5,5), 0, math.random(-5,5)))
end
function updateTarget()
TargetPlayer = findTargetPlayer()
if TargetPlayer then
findPathToTarget()
end
end
while true do
updateTarget()
updateWaypoint()
wait(0.1)
end
I've tried various solutions, including changing the variable types and removing potential lists, but nothing has worked so far. I expected the script to run without errors and for the NPC to follow the player.

For loop not woking? Roblox studio

Code:
local DataStoreService = game:GetService("DataStoreService")
local InvDataStore = DataStoreService:GetDataStore("InvDataStore")
game.Players.PlayerAdded:Connect(function(player)
local Id = player.UserId
local Inventory = Instance.new("Folder")
Inventory.Name = "Inventory"
Inventory.Parent = player
local Inv = InvDataStore:GetAsync(Id)
print(Inv)
print(table.concat(Inv, " "))
end)
game.Players.PlayerRemoving:Connect(function(player)
local Id = player.UserId
local InvTable = {}
for i, v in pairs(game.Players:FindFirstChild(player.Name).Inventory:GetChildren()) do
print("Repear")
if v:IsA("NumberValue") then
table.insert(InvTable, v)
print(v)
end
end
print(InvTable)
print(table.concat(InvTable, " "))
InvDataStore:SetAsync(Id, InvTable)
end)
Output:
13:25:35.288 - Untitled Game auto-recovery file was created
Realism Mod is currently running v2.09! (x2)
table: 0x08cb53598b2d3aa1
table: 0xd8ce847b521d4091
1
13:26:26.703 - Disconnect from ::ffff:127.0.0.1|60556
Explorer:
It seems to be skipping this loop:
for i, v in pairs(game.Players:FindFirstChild(player.Name).Inventory:GetChildren()) do
print("Repear")
if v:IsA("NumberValue") then
table.insert(InvTable, v)
print(v)
end
end
as it seems to not print repear (repeat) OR v (Value) anyone know whats up?
Note: The thing i dont understand, is it doesnt print the value after the save, and before the save, and forgetting the for loop. I can provide extra things to.
It is ignoring the loop because when it gets to game.Players:FindFirstChild(player.Name) the return will be nil, due to that player just leaved the server. What you can try to do is iterating directly from the player object you have, you don't need to look for the object player if you already have it.
Try:
for i, v in pairs(player.Inventory:GetChildren()) do
print("Repear")
if v:IsA("NumberValue") then
table.insert(InvTable, v)
print(v)
end
end
also it is a good practice to store data during the game and not when it leaves, all those objects are removed too when the player leaves, it is better to handle the table during the game and during player removing just update the data store.
Also a good practice is to update datastore for the player every ~5 minutes

Effect not showing up on guis

I'm making an effect in my game where it scrolls through some choices and slows down to a stop on one choice.
There's 4 screens and I want each one to play the effect simultaneously, all the guis show up at the same time but the effect never plays. I've marked the part of the code that does the effect in the block of code below:
message.chooseduel = function(spins)
local lobby=workspace.Lobby
local screens=lobby.Screens
local n1,n2
for _, screen in pairs(screens:GetChildren()) do
local gui=screen.SurfaceGui
local ds=gui.DuelScreen
gui.Enabled=true
for i, v in pairs(ds.Container:GetChildren()) do
local ll
local lastpicked
local t = ds.Container:GetChildren()
local menuItems = #t -- number of menu items
local repeats = 1 -- Repeated
for R = 65 + spins, 1, -1 do
ll = t[repeats]
if ll:IsA("GuiObject") then
--**effect**--
local newgui = coroutine.wrap(function()
print("HI!")
ll.BackgroundColor3=Color3.fromRGB(130, 125, 56)
wait( R^-.7*.7 ) --
ll.BackgroundColor3=ll.BorderColor3
repeats = repeats % menuItems + 1
end)
newgui()
--**effect**--
end
end
ll = t[repeats]
ll.BackgroundColor3=Color3.fromRGB(230, 225, 156)
n1=string.sub(ll.n1.Image,64)
n2=string.sub(ll.n2.Image,64)
print("Returning:",n1,n2)
end
end
wait(2)
return {n1,n2}
end
Hope this helps:
message.chooseduel = function(spins)
spins = math.ceil(spins) -- just making sure.
local lobby=workspace.Lobby
local screens=lobby.Screens
local n1,n2
for _, screen in pairs(screens:GetChildren()) do
local gui=screen.SurfaceGui
local ds=gui.DuelScreen
gui.Enabled=true
spawn(function() -- I think this is where the coroutine / async function should start
local ll
local lastpicked -- Variable not used
local t = ds.Container:GetChildren()
local numMenuItems = #t -- number of menu items
local current = 1 -- Repeated
print("HI!")
for R = 65 + spins, 1, -1 do
ll = t[current]
if ll:IsA("GuiObject") then
ll.BackgroundColor3=Color3.fromRGB(130, 125, 56)
wait( R^-.7*.7 ) --
ll.BackgroundColor3=ll.BorderColor3
current = current % numMenuItems + 1
end
end
print("BYE!")
ll = t[current]
ll.BackgroundColor3=Color3.fromRGB(230, 225, 156)
n1=string.sub(ll.n1.Image,64) -- um... Interesting. wait what?
n2=string.sub(ll.n2.Image,64)
print("Returning:",n1,n2)
end)
end
wait(2)
return {n1,n2}
end
I'm not sure I totally get what you are doing here or how you have things set up, but in general you should try to move coroutines / spawned functions to the outsides of loops.

Join tiles in Corona SDK into one word for a Breakout game grid?

I have a game project to re-implement Breakout. I want to display two words, each word on a line. They are joined by the bricks block. Inside, the top line is the first name, aligned left. The bottom line is the last name, aligned right. They are input from textboxes, and rendered as shown:
Each second that passes, the screen will add a configurable number of bricks to the grid (for example, five bricks per second) until the two words appear complete. I displayed a letter of the alphabet which is created from the matrix(0,1).
...But I don’t know how to join them into one word. How can I join these letters?
This is what I've gotten so far:
Bricks.lua
local Bricks = display.newGroup() -- static object
local Events = require("Events")
local Levels = require("Levels")
local sound = require("Sound")
local physics = require("physics")
local Sprites = require("Sprites")
local Func = require("Func")
local brickSpriteData =
{
{
name = "brick",
frames = {Sprites.brick}
},
{
name = "brick2",
frames = {Sprites.brick2}
},
{
name = "brick3",
frames = {Sprites.brick3}
},
}
-- animation table
local brickAnimations = {}
Sprites:CreateAnimationTable
{
spriteData = brickSpriteData,
animationTable = brickAnimations
}
-- get size from temp object for later use
local tempBrick = display.newImage('red_apple_20.png',300,500)
--local tempBrick = display.newImage('cheryGreen2.png',300,500)
local brickSize =
{
width = tempBrick.width,
height = tempBrick.height
}
--tempBrick:removeSelf( )
----------------
-- Rubble -- needs to be moved to its own file
----------------
local rubbleSpriteData =
{
{
name = "rubble1",
frames = {Sprites.rubble1}
},
{
name = "rubble2",
frames = {Sprites.rubble2}
},
{
name = "rubble3",
frames = {Sprites.rubble3}
},
{
name = "rubble4",
frames = {Sprites.rubble4}
},
{
name = "rubble5",
frames = {Sprites.rubble5}
},
}
local rubbleAnimations = {}
Sprites:CreateAnimationTable
{
spriteData = rubbleSpriteData,
animationTable = rubbleAnimations
}
local totalBricksBroken = 0 -- used to track when level is complete
local totalBricksAtStart = 0
-- contains all brick objects
local bricks = {}
local function CreateBrick(data)
-- random brick sprite
local obj = display.newImage('red_apple_20.png')
local objGreen = display.newImage('cheryGreen2.png')
obj.name = "brick"
obj.x = data.x --or display.contentCenterX
obj.y = data.y --or 1000
obj.brickType = data.brickType or 1
obj.index = data.index
function obj:Break()
totalBricksBroken = totalBricksBroken + 1
bricks[self.index] = nil
obj:removeSelf( )
sound.play(sound.breakBrick)
end
function obj:Update()
if(self == nil) then
return
end
if(self.y > display.contentHeight - 20) then
obj:Break()
end
end
if(obj.brickType ==1) then
physics.addBody( obj, "static", {friction=0.5, bounce=0.5 } )
elseif(obj.brickType == 2) then
physics.addBody( objGreen,"static",{friction=0.2, bounce=0.5, density = 1 } )
end
return obj
end
local currentLevel = testLevel
-- create level from bricks defined in an object
-- this allows for levels to be designed
local function CreateBricksFromTable(level)
totalBricksAtStart = 0
local activeBricksCount = 0
for yi=1, #level.bricks do
for xi=1, #level.bricks[yi] do
-- create brick?
if(level.bricks[yi][xi] > 0) then
local xPos
local yPos
if(level.align == "center") then
--1100-((99*16)*0.5)
xPos = display.contentCenterX- ((level.columns * brickSize.width) * 0.5/3) + ((xi-1) * level.xSpace)--display.contentCenterX
--xPos = 300 +(xi * level.xSpace)
yPos = 100 + (yi * level.ySpace)--100
else
xPos = level.xStart + (xi * level.xSpace)
yPos = level.yStart + (yi * level.ySpace)
end
local brickData =
{
x = xPos,
y = yPos,
brickType = level.bricks[yi][xi],
index = activeBricksCount+1
}
bricks[activeBricksCount+1] = CreateBrick(brickData)
activeBricksCount = activeBricksCount + 1
end
end
end
totalBricks = activeBricksCount
totalBricksAtStart = activeBricksCount
end
-- create bricks for level --> set from above functions, change function to change brick build type
local CreateAllBricks = CreateBricksFromTable
-- called by a timer so I can pass arguments to CreateAllBricks
local function CreateAllBricksTimerCall()
CreateAllBricks(Levels.currentLevel)
end
-- remove all brick objects from memory
local function ClearBricks()
for i=1, #bricks do
bricks[i] = nil
end
end
-- stuff run on enterFrame event
function Bricks:Update()
-- update individual bricks
if(totalBricksAtStart > 0) then
for i=1, totalBricksAtStart do
-- brick exists?
if(bricks[i]) then
bricks[i]:Update()
end
end
end
-- is level over?
if(totalBricksBroken == totalBricks) then
Events.allBricksBroken:Dispatch()
end
end
----------------
-- Events
----------------
function Bricks:allBricksBroken(event)
-- cleanup bricks
ClearBricks()
local t = timer.performWithDelay( 1000, CreateAllBricksTimerCall)
--CreateAllBricks()
totalBricksBroken = 0
-- play happy sound for player to enjoy
sound.play(sound.win)
print("You Win!")
end
Events.allBricksBroken:AddObject(Bricks)
CreateAllBricks(Levels.currentLevel)
return Bricks
Levels.lua
local Events = require("Events")
local Levels = {}
local function MakeLevel(data)
local level = {}
level.xStart = data.xStart or 100
level.yStart = data.yStart or 100
level.xSpace = data.xSpace or 23
level.ySpace = data.ySpace or 23
level.align = data.align or "center"
level.columns = data.columns or #data.bricks[1]
level.bricks = data.bricks --> required
return level
end
Levels.test4 = MakeLevel
{
bricks =
{
{0,2,0,0,2,0,0,2,0},
{0,0,2,0,2,0,2,0,0},
{0,0,0,0,2,0,0,0,0},
{1,1,2,1,1,1,2,1,1},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0},
}
}
Levels.test5 = MakeLevel
{
bricks =
{
{0,0,0,1,0,0,0,0},
{0,0,1,0,1,0,0,0},
{0,0,1,0,1,0,0,0},
{0,1,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{1,0,0,0,0,0,1,0},
{1,0,0,0,0,0,1,0},
{1,0,0,0,0,0,1,0},
{1,0,0,0,0,0,1,0}
}
}
-- Levels.test6 = MakeLevel2
-- {
-- bricks =
-- {
----A "a" = {{0,0,0,1,0,0,0,0},
-- {0,0,1,0,1,0,0,0},
-- {0,0,1,0,1,0,0,0},
-- {0,1,0,0,0,1,0,0},
-- {0,1,1,1,1,1,0,0},
-- {1,0,0,0,0,0,1,0},
-- {1,0,0,0,0,0,1,0},
-- {1,0,0,0,0,0,1,0},
-- {1,0,0,0,0,0,1,0}},
----B
-- "b" = {{1,1,1,1,0,0,0},
-- {1,0,0,0,1,0,0},
-- {1,0,0,0,1,0,0},
-- {1,0,0,0,1,0,0},
-- {1,1,1,1,0,0,0},
-- {1,0,0,0,1,0,0},
-- {1,0,0,0,0,1,0},
-- {1,0,0,0,0,1,0},
-- {1,1,1,1,1,0,0}},
--...........
--.......
--...
-- --Z
-- "z"= {{1,1,1,1,1,1,1,0},
-- {0,0,0,0,0,1,0,0},
-- {0,0,0,0,1,0,0,0},
-- {0,0,0,0,1,0,0,0},
-- {0,0,0,1,0,0,0,0},
-- {0,0,1,0,0,0,0,0},
-- {0,0,1,0,0,0,0,0},
-- {0,1,0,0,0,0,0,0},
-- {1,1,1,1,1,1,1,0}}
-- }
-- }
-- stores all levels in ordered table so that one can be selected randomly by index
Levels.levels =
{
--Levels.test4,
Levels.test5
-- Levels.test6,
}
function Levels:GetRandomLevel()
return self.levels[math.random(#Levels.levels)]
end
Levels.notPlayedYet = {}
Levels.currentLevel = Levels:GetRandomLevel()
-- Events
function Levels:allBricksBroken(event)
self.currentLevel = Levels:GetRandomLevel()
end
Events.allBricksBroken:AddObject(Levels)
return Levels
The work I've done thus far (same as above) as an external download: http://www.mediafire.com/download/1t89ftkbznkn184/Breakout2.rar
In the interest of actually answering the question:
I'm not 100% sure what you mean by "How can I join these letters", but from poking through the code I have a guess, so please clarify on whether it is accurate, or if I am wrong about what you wanted.
Scenario 1
You haven't successfully achieved the image illustrated in the screenshot - you've been able to draw one letter, but not multiple ones.
In this case, you'll need to have a better understanding of what your code is doing. The CreateBricksFromTable function takes in a Level object, which is created by the MakeLevel function from a table with a bricks property, which is a table of tables that represent rows with columns in them, showing what type of brick should be at each position. In your commented-out level, you have created an table where the bricks field contains a field for each letter, but the MakeLevel function still expects a bricks field that directly contains the grid of blocks. You will have to - as it seems you attempted - create a MakeWordLevel function (or the like) that takes this letter list, and a word for each line, and constructs a larger grid by copying the appropriate letters into it.
StackOverflow is not your programming tutor, and an SO question is not the right forum for having people write code for you or getting into step-by-step details of how to do this, but I'll leave you a basic outline. Your function would look something like this:
local function MakeWordLevel(data, line1, line2)
local level = {}
...
return level
end
And then would have to:
Populate all of the same properties that MakeLevel does
Calculate how wide (level.columns) the level should be with all the letters
Create a table in the same format as the bricks properties, but big enough to hold all of the letters
Go through the input strings (line1 and line2), find the correct letter data from what is now the test6 array, and copy that data into the large table
Assign that table as level.bricks
This question already is a bit outside of what StackOverflow is intended for in that it asks about how to implement a feature rather than achieve a small, specific programming task, so any further followup should take place in a chatroom - perhaps the Hello World room would be helpful.
Scenario 2:
This was my original guess, but after considering and reading past edits, I doubt this is answering the right question
You may want a solid "background" of, say, red blocks, surrounding your letters and making the field into a solid "wall", with the name in a different color. And you may want these bricks to slowly show up a few at a time.
In that case, the main thing you need to do is keep track of what spaces are "taken" by the name bricks. There are many ways to do this, but I would start with a matrix to keep track of that - as big as the final playing field - full of 0's. Then, as you add the bricks for the name, set a 1 at the x,y location in that matrix according to that block's coordinate.
When you want to fill in the background, each time you go to add a block at a coordinate, check that "taken" matrix before trying to add a block - if it's taken (1), then just skip it and move onto the next coordinate.
This works if you're filling in the background blocks sequentially (say, left to right, top to bottom), or if you want to add them randomly. With random, you'd also want to keep updating the "taken" matrix so you don't try to add a block twice.
The random fill-in, however, presents its own problem - it will keep taking longer to fill in as it goes, because it'll find more and more "taken" blocks and have to pick a new one. There are solutions to this, of course, but I won't go too far down that road when I don't know if that's even what you want.
I don't really understand (or read, for that matter) your code but from what I see joining them into complete words is easy. You have two possibilities.
You can "render" them directly into your level/display data, simply copy them to the appropriate places, like this:
-- The level data.
local level = {}
-- Create the level data.
for row = 1, 25, 1 do
local rowData = {}
for column = 1, 80, 1 do
rowData[column] = "."
end
level[row] = rowData
end
-- Now let us setup the letters.
local letters = {
A = {
{".",".",".","#",".",".",".","."},
{".",".","#",".","#",".",".","."},
{".",".","#",".","#",".",".","."},
{".","#",".",".",".","#",".","."},
{".","#","#","#","#","#",".","."},
{"#",".",".",".",".",".","#","."},
{"#",".",".",".",".",".","#","."},
{"#",".",".",".",".",".","#","."},
{"#",".",".",".",".",".","#","."}
},
B = {
{"#","#","#","#",".",".","."},
{"#",".",".",".","#",".","."},
{"#",".",".",".","#",".","."},
{"#",".",".",".","#",".","."},
{"#","#","#","#",".",".","."},
{"#",".",".",".","#",".","."},
{"#",".",".",".",".","#","."},
{"#",".",".",".",".","#","."},
{"#","#","#","#","#",".","."}
}
}
-- The string to print.
local text = "ABBA"
-- Let us insert the data into the level data.
for index = 1, #text, 1 do
local char = string.sub(text, index, index)
local charData = letters[char]
local offset = index * 7
for row = 1, 9, 1 do
local rowData = charData[row]
for column = 1, 7, 1 do
level[row][offset + column] = rowData[column]
end
end
end
-- Print everything
for row = 1, 25, 1 do
local rowData = level[row]
for column = 1, 80, 1 do
io.write(rowData[column])
end
print()
end
You save you letters in a lookup table and then copy them, piece by piece, to the level data. Here I replaced the numbers with dots and number signs to make it prettier on the command line.
Alternately to that you can also "render" the words into a prepared buffer and then insert that into the level data by using the same logic.

Generate a random number, but every random seconds in LUA/Love2d

I am trying to generate a random number, every random seconds (the final purpose would be to change the color of a block from time to time, randomly).
For that, I am using the Hump library (http://vrld.github.io/hump/#hump.timer).
Here is my code at the moment, I am true beginner in LUA/Love2d coding.
It generates a number, and displays it every seconds, instead of every random seconds... (but the random seconds is also generated).
I don't really understand why it is not working.
local Timer = require "timer"
function love.load()
text="t"
number2=1
end
local f = function()
math.randomseed(os.time())
number = math.random( 2,10 )
text="in " .. number2 .. " seconds (random)... random number =" .. number
return true
end
function love.update(dt)
number2 = math.random( 2,4 )
Timer.update(number2)
Timer.addPeriodic(number2, f)
end
function love.draw()
love.graphics.print( text, 330, 300 )
end
Thanks for any help !
While I am not familiar with Hump, it seems that you can easily use the timer's add function for your purpose, as it will call the function exactly once after x seconds, allowing you to schedule the next execution with a different delay:
local timer = require("timer")
local text = ""
local function tick()
-- Generate random number
local newNumber = math.random(2, 10)
local newDelay = math.random(2, 4)
text = ("Current number: %d, next in %d seconds!"):format(newNumber, newDelay)
-- Actually schedule the next call
timer.add(newDelay, tick)
end
function love.load()
tick()
end
function love.update(dt)
timer.update(dt)
end
function love.draw()
love.graphics.print(text, 330, 300)
end

Resources