I am new to corona and I am trying to make multiple objects get moving, and selectively make them rotate one at a time. I should be able to select any one object by tapping on it, and then by clicking a button, it should rotate by X degree (for each click). I have the following code working for one object. But stuck on getting the setup for multiple moving objects. I apologize if it is basic.
local Button = display.newRect(200,200, 10, 40)
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)
Button:addEventListener("tap", rotate)
Just update your code to have a "selected" object, like this:
local selectedObject = nil
local Button = display.newRect(200,200, 10, 40)
local obj = display.newRect(50,50, 10, 40)
local SPEED = 1
local function select(event)
selectedObject = event.target
end
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)
if selectedObject then
selectedObject.rotation = selectedObject.rotation + 45
end
end
obj:addEventListener("tap", select)
Runtime:addEventListener("enterFrame", move)
Button:addEventListener("tap", rotate)
Related
With the current complexity of scripting, and my abilities, i have hit a dead end with working out how to create a sliding door in the Popular MMO, "Roblox".
I have analyzed the script automatically, and manually, and have not found any errors.
Here it is below:
local Part = workspace.Part
local newPos = Part.Position + Vector3.new(-61.866, 8.551, -97.181)
local Time = 5
local Increment = 0.5
local Debounce = false
local Diff = newPos - Part.Position
local Mag = Diff.magnitude
local Direction = CFrame.new(Part.Position, newPos).lookVector
function MovePart()
if Debounce then return end
Debounce = true
for n = 0, Mag, Increment do
Part.CFrame = Part.CFrame + (Direction * Increment)
wait( (Time/Mag) * Increment )
end
Debounce = false
end
workspace.Button.ClickDetector.MouseClick:connect(MovePart)
When i have inserted a Button with a Clickdetector inside, and tried to click the button, no results have been shown- not even an error! I am stuck, and require assistance. It would be much appreciated.
Hmmm, works for me.
Is it already at (-61.866, 8.551, -97.181)?
Is there another "Part" or "Button"?
Is it not anchored and something in the way?
Alternate solution:
Put the button, part, and the following script together in a model.
local Part = script.Parent.Part
local Button = script.Parent.Button
Part.Anchored = true
local Direction = Part.CFrame.lookVector
local Mag = Part.Size.Z
local Time = 5
local Increment = 0.5 -- Smaller will make smoother 'movement'
local Debounce = false
function MovePart()
if Debounce then return end
Debounce = true
for n = 0, Mag, Increment do
Part.CFrame = Part.CFrame + (Direction * Increment)
wait(Time/(Mag * Increment))
end
for n = 0, Mag, Increment do
Part.CFrame = Part.CFrame + (Direction * -Increment) -- so that it moves back
wait(Time/(Mag * Increment))
end
Debounce = false
end
clickD = Button.ClickDetector or Instance.new("ClickDetector",Button)
clickD.MouseClick:connect(MovePart)
Hope that helps.
all you have to do is place a part in the workspace, rename it "Button" and insert a clickDetector into the part.
Im attempting to make a simple game app and keep running into this problem. I stopped programming in Lua for a few years so I don't exactly remember how to fix this. Anyway, my code is as follows:
EDIT: Here is the entire file. Still trying to figure out the formatting of Stack Overflow. Error occurs at line 76.
module(..., package.seeall)
-- Main function - MUST return a display.newGroup()
function new()
local localGroup = display.newGroup()
---------
local Rad = math.rad
local Sin = math.sin
local Cos = math.cos
local Pi = math.pi
local Atan2 = math.atan2
local radD = 180 / Pi
local DegR = Pi / 180
local touchPoint = display.newCircle(localGroup, -50, -50, 20)
touchPoint.isFocus = false
touchPoint.alpha = 0
function GetDistanceFromObjects(obj1, obj2)
local xDist = obj1.x - obj2.x
local yDist = obj1.y - obj2.y
local dist = Sqrt((xDist * xDist) + (yDist * yDist))
return dist
end
function getAngleDeg(inX1, inY1, inX2, inY2)
local xDist = inX2 - inX1
local yDist = inY2 - inY1
local angRad = Atan2(yDist, xDist)
return angRad * radD + 90
end
require "sprite"
function VectorFromAngle(inAngle, inVelocity)
local vx = Cos(Rad(inAngle-90))
local vy = Sin(Rad(inAngle-90))
if(inVelocity ~= nil)then
vx = vx * inVelocity
vy = vy * inVelocity
end
return vx,vy
end
require ( "physics" )
physics.start()
physics.setGravity( 1, 1 )
--( x, y )
--physics.setDrawMode ( "hybrid" )
math.randomseed(os.time())
local background = display.newImage("yazd.jpeg")
localGroup:insert(background)
--width of image divided by # of pics lined up from left to right (in the sprite) = the first #
--height of image divided by # of pics lined up from top to bottom (in the sprite) = the second #
local birdSheet = sprite.newSpriteSheet( "enemy.jpg", 59, 50 )
local birdSet = sprite.newSpriteSet(birdSheet, 1, 1)
-- images 1-14
sprite.add( birdSet, "bird", 1, 1, 200, 0 )
-- play 1-14, each image every 200 ms, 0 = loop count, which is infinite
local bird1 = sprite.newSprite( birdSet )
bird1.x = 40 -- starting point
bird1.y = 40 -- starting point
bird1.xScale = 0.5 --scale down x
bird1.yScale = 0.5 --scale down y
bird1:prepare("bird") --prepare sprite sequence
bird1:play() --play sprite
localGroup:insert(bird1)
--only local to this group
local killSheet = sprite.newSpriteSheet("explosion.png", 100, 100)
local killSet = sprite.newSpriteSet(killSheet, 1, 9)
sprite.add(killSet, "kill", 1, 9, 200, 1)
local birdCount = 1
local transDirection12
local function transDirection1()
bird1.xScale = 0.5
transition.to(bird1, {time=math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection12})
end
transDirection12 = function()
bird1.xScale = 0.5
transition.to(bird1, {time= math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection1})
end
transDirection1()
-- local transDirection1 declares what will be used (local function)
-- transDirection1 = function
-- following it are the function qualities
-- declares it will use object/image called bird1 and scales it to .5
-- time = ____ means it will take a certain time, between ____ and ____ to complete the transition
-- x=____ means that is where it will move to on the x axis
-- y=____ means that is where it will move to on the y axis
-- alpha = ___ means the is how transparent it will be
-- onComplete = ________ means that when the action is complete, it will call another function
-- The next function has the same qualities as transDirection1, but the onComplete part calls transDirection1 and they continue to loop
-- transDirection1() declares transDirection1 so the app knows about it and can use it
-- the other trans do not need to be declared because they are part of transDirection1, which is already declared
--(x, y, size.x, size.y)
local player = display.newImage( "mk11.png" )
player.x = 240
player.y = 260
player.xScale = .5
player.yScale = .5
localGroup:insert( player )
-- add physics to all the objects wanted: (object wanted, "static" or "dynamic")
physics.addBody(player, "static", {radius=30, isSensor = true})
physics.addBody(bird1, "static", {radius=23})
local function shoot(inPointX, inPointY)
-- (start at the x of the player + 10, also start at the y of the player, the radius of the circle is 5)
local bullet = display.newImage( "bullet2.png" )
bullet.x = player.x
bullet.y = player.y
-- add physics to the object, which is the bullet.
-- Make the bullet "dynamic" or moving
physics.addBody(bullet, "dynamic")
bullet.isFixedRotation = true
localGroup:insert( bullet )
local velocity = 300
local vx, vy = VectorFromAngle(player.rotation, velocity)
bullet.rotation = player.rotation
bullet:setLinearVelocity(vx, vy)
end
function RotateToTouchPoint(inPointX, inPointY)
local ang = getAngleDeg(player.x, player.y, inPointX, inPointY)
player.rotation = ang
end
local function ScreenTouchListener(event)
local phase = event.phase
if(phase == "began")then
if(touchPoint.isFocus == false)then
touchPoint.alpha = 1
touchPoint.x = event.x
touchPoint.y = event.y
display.getCurrentStage():setFocus(touchPoint, event.id)
touchPoint.isFocus = true
RotateToTouchPoint(event.x, event.y)
shoot(event.x, event.y)
end
elseif(touchPoint.isFocus)then
if(phase == "moved")then
touchPoint.x = event.x
touchPoint.y = event.y
RotateToTouchPoint(event.x, event.y)
elseif(phase == "ended" or phase == "cancelled")then
display.getCurrentStage():setFocus(touchPoint, nil)
touchPoint.isFocus = false
touchPoint.alpha = 0
end
end
return true
end
local function gotShot (event)
event.target:removeSelf()
event.other:removeSelf()
local explosion = sprite.newSprite(killSet)
explosion.x, explosion.y = event.target.x, event.target.y
explosion:prepare("kill")
explosion:play()
localGroup:insert( explosion )
birdCount = birdCount - 1
-- when there are no more birds, remove the runtime event listener and perform the
-- function with a delay of 500 m.s. The function changes the scene to test.lua
if "ended" then
if birdCount == 0 then
Runtime:removeEventListener("touch", ScreenTouchListener)
timer.performWithDelay(500, function()
director:changeScene("mainPage") end, 1)
end
end
end
bird1:addEventListener("collision", gotShot)
Runtime:addEventListener("touch", ScreenTouchListener)
---------
-- MUST return a display.newGroup()
return localGroup
end
Any help is appreciated!
The error message is perfectly clear -- the variable sprite used at this line:
local bird1 = sprite.birdSheet( birdSet )
has a nil value, meaning it has not been initialized or was set to nil. You need to show the earlier code where you should have set it up.
(After OP updates)
I think this line
require "sprite"
should actually be
sprite = require "sprite"
You can read more in modules tutorial here:
http://lua-users.org/wiki/ModulesTutorial
I have just started to learn LUA in school, but i cannot find many helpful tutorials o the internet to aid in my learning. I have made a simple game (which doesn't work yet, i realize that) and a main menu. However, when i try to start the app, it gives me this error:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: attempt to index global 'showCredits' (a nil value)
stack traceback:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: in main chunk
[Finished in 9.4s]
I have looked the error, and I cannot seem to understand how to fix it. The other questions have said something about returning the function returning a nil value, and that I should add a return statement to the end, but that doesn't work either.
Here is the code # line 47.
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
Here is my full code, in case the problem lies elsewhere:
display.setStatusBar(display.HiddenStatusBar)
radius = 40
smallTime = 200
bigTime = 800
score = 0
scoreInc = 2000
--HomePage
local name
local playButton
local creditsButton
local homePage
--Credits
local creditsPage
--Sounds
local circleSpawn = audio.loadSound( "circle_spawn.wav" )
local circleTap = audio.loadSound( "circle_tap.wav" )
function Main()
name = display.newImage('title.png', display.contentWidth / 2, 53)
name:scale( .5, .5 )
playButton = display.newImage('playButton.png', display.contentWidth / 2, 245)
playButton:scale( .5, .5 )
creditsButton = display.newImage('creditsButton.png', display.contentWidth / 2, 305)
creditsButton:scale( .5, .5 )
homePage = display.newGroup(name, playButton, creditsButton)
startButtonListeners('add')
end
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
function hideCredits.touch(e)
transition.to(creditsView, {time = 300, y = display.contentHeight, onComplete = function() creditsButton.isVisible = true playButton.isVisible = true creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
transition.to(name, {time = 300, y = lastY});
end
function startButtonListeners(action)
if(action == 'add') then
playButton:addEventListener('touch', playGame)
creditsButton:addEventListener('touch', showCredits)
else
playButton:removeEventListener('touch', playGame)
creditsButton:removeEventListener('touch', showCredits)
end
end
Main()
printScore = display.newText("Score: " .. tostring(score), display.contentWidth-80, 40, native.systemFontBold, 20)
-- A function that creates random circles
function generateCircle ()
-- Creates a new circle between 0 (the left most bounds) and the width of the display (being the content width), and also
-- 0 (the upper most bounds) and the height of the display (being the content height). The radius of the circle is 'radius'
x = math.random(radius, display.contentWidth-radius)
y = math.random(80, display.contentHeight)
score = score + scoreInc
myCircle = display.newCircle( x, y, radius )
myCircle:setFillColor( math.random(), math.random(), math.random() )
delayTime = math.random(smallTime, bigTime)
score = score + scoreInc
printScore.text = "Score:"..tostring(scores)
local spawnChannel = audio.play( circleSpawn )
timer.performWithDelay( delayTime, generateCircle )
end
generateCircle()
function myCircle:touch( event )
local tapChannel = audio.play( circleTap )
myCircle:removeSelf()
end
myCircle:addEventListener( "touch", myCircle )
The answer by greatwolf will work. But just a tip from my experience. One way I like to create functions is to try to define the name of the function first near the top of the lua file. Then I will define the function later on in the file. Something like this:
--function preallocation
local onPlayTap
local onSoundOnTap
local onSoundOffTap
local onCreditsTap
local onHelpTap
---------------------------------------------------------------------------------
-- Custom Function Definitions
---------------------------------------------------------------------------------
--Called when Sound On Button is tapped, turn off sound
onSoundOnTap = function(event)
end
--Called when Sound Off Button is tapped, turn on sound
onSoundOffTap = function(event)
end
--Called when Credits button is tapped, shows credits
onCreditsTap = function(event)
end
--Called when Help button is tapped, shows help
onHelpTap = function(event)
end
--Callback to Play button. Moves scene to Level Picker Scene
onPlayTap = function(event)
end
What this does is allow each function to be called by any other function in the file. If you do it the way you are doing it by adding the function name before the function like so:
local showCredits = {}
function showCredits.touch(e)
end
local hideCredits = {}
function hideCredits.touch(e)
end
your showCredit function will not be able to call the hideCredits function below it because the hideCredits variable has not been defined yet when the showCredit function was defined. Although this may not effect your current game, in future apps or games, you may need to call functions inside of other functions. To make this work properly, predefine all your function variables first, then define all your function afterwards. Hope this helps.
I need some help understanding how would I reset the score of numMiss, numHit, and numPercent back to 0 as soon as I tap the "reset.png" button and while doing so will also start the game from the beginning again.
Also, let me know if there are any corrections to be made within my code.
Heres what I have of the code so far
--width and height
WIDTH = display.contentWidth --320
HEIGHT = display.contentHeight --480
--display background
local p = display.newImageRect("park.png" ,500, 570)
p.x = WIDTH/2
p.y = HEIGHT/2
--display bouncing dog
local RADIUS = 5
local d = display.newImageRect("dogeball.png", 70, 70)
d.x = 50
d.y = 100
--display treat
local t = display.newImageRect("treat.png", 50, 50)
t.x = 245
t.y = math.random(HEIGHT)
--displays the reset button
local r = display.newImageRect("reset.png", 100,100)
r.x = 280
r.y = 480
--starting value of gravity and bounce(will change)
local GRAVITY = 0.3
local BOUNCE = 0.75
--downward force
local velocity = 0
--Tells the score to reset when true
local reset = false
--shows number of hits
local numHit = 0
--shows number of misses
local numMiss = 0
--Gets Percentage score
local numPercent = 0
--make hits and misses display
scoreHits = display.newText("Hits = " .. numHit, WIDTH/7, 1, native.systemFont, 18)
scoreMisses = display.newText("Misses = " .. numMiss, WIDTH/2.1, 1, native.systemFont, 18)
scorePercent = display.newText("Hit % = " .. numPercent, WIDTH/1.2, 1, native.systemFont, 18)
function enterFrame()
d.y = d.y + velocity
velocity = velocity + GRAVITY
local HIT_SLOP = RADIUS * 8 -- Adjust this to adjust game difficulty
if math.abs(t.x - d.x) <= HIT_SLOP
and math.abs(t.y - d.y) <= HIT_SLOP then
numHit = numHit + 1
scoreHits.text = "Hits = " .. numHit
--count 1 hit once dog and treat hit eachother
if (t.x - d.x) <= HIT_SLOP and (t.y - d.y) <= HIT_SLOP then
t.x = 400 --resets treat postioning
t.y = math.random(HEIGHT) --gives treat a random y coordinate
end
end
--puts the barrier at the bottom of the screen and tells dog to bounce from there
if (d.y > HEIGHT) then
d.y = HEIGHT
velocity = -velocity * BOUNCE
end
t.x = t.x - 5 --speed treat goes
if t.x < -350 then--position of the treat
t.x = 400
scoreMisses.text = "Misses = " .. numMiss
else if t.x < -100 then
t.y = math.random(HEIGHT) --random height after treat goes past dog
else if t.x < -99 then
numMiss = numMiss + 1 --calculates misses when goes past screen
scoreMisses.text = "Misses = " .. numMiss
end
end
end
--calculate percentage hits
numPercent = 100 * numHit / (numHit + numMiss)
scorePercent.text = "Hit % = " .. math.round(numPercent) --prints and rounds percentage
function tapped(event) --when tapped on reset, score gets reset
--reset function goes here
end
end
r:addEventListener( "tap", tapped )
end
function touched(event)
-- print(event.phase)
if event.phase == "began" then
velocity = velocity - 6 -- thrusts dog
end
return true
end
Runtime:addEventListener( "enterFrame" , enterFrame )
Runtime:addEventListener( "touch", touched )
To make your image a button you need to add an event listener that responds to touch or tap events.
see http://docs.coronalabs.com/api/event/touch/index.html
Or you use the widget library which gives you the possibility to use a blank button background and set only the label for each button what will be very handy when you include translations for other languages.
see http://docs.coronalabs.com/api/library/widget/newButton.html
In my game I have a function gameInit() that sets the hole game and all variables. This function is called when the game starts and also when the player wants to do a reset as it over writes the old variables. (there are other technics, depending on the complexity of your game and if you for example want to store the game settings for the next time the player starts the game)
I'm pretty new and have looked up my question to no avail, What I have are objects like balloons that float from bottom of the screen to the top, my problem is the spawnBallons function is only called once so only one object appears I want to call the function multiple times to spawn multiple objects and increase the number of objects spawned every 20 seconds?
function spwanBalloons()
local allBalloons = {"green_balloon2.png", "red_balloon.png"}
ballons = display.newImage(allBalloons[math.random(#allBalloons)])
ballons.x = math.random(display.contentWidth)
ballons.y = display.contentHeight + 60
transition.to( ballons, { time=math.random(3500-speedBump, 4500-speedBump), y=-100} )
speedBump = speedBump + 15
end
function startGame()
scoreText = display.newText( "Score: 0", 0, 0, "Helvetica", 22 )
scoreText.x = centerX
scoreText.y = display.screenOriginY + 10
spwanBalloons()
end
I tried this timer.performWithDelay( 500, spwanBalloons, 50 ) But All it does is spawn 50 objects over a half a second, I want to spawn a random amount of object until I tell it to stop?
This should spawn 1 balloon every half second for 20 seconds, then 2 balloons every half second, etc.
You can stop the timers with stopSpawnIncrease() and stopSpawn() as needed.
local spawnIncreaseTimer
local spawnNumber=0
local function spawnIncrease()
spawnNumber=spawnNumber+1
spawnIncreaseTimer = timer.performWithDelay( 20000, spawnIncrease)
end
function stopSpawnIncrease()
timer.cancel( spawnIncreaseTimer )
end
local spawnTimer
function spwanBalloons()
for i=1,spawnNumber do
local allBalloons = {"green_balloon2.png", "red_balloon.png"}
ballons = display.newImage(allBalloons[math.random(#allBalloons)])
ballons.x = math.random(display.contentWidth)
ballons.y = display.contentHeight + 60
transition.to( ballons, { time=math.random(3500-speedBump, 4500-speedBump), y=-100} )
speedBump = speedBump + 15
end
spawnTimer = timer.performWithDelay( 500, spwanBalloons )
end
function stopSpawnTimer()
timer.cancel( spawnTimer )
end
function startGame()
scoreText = display.newText( "Score: 0", 0, 0, "Helvetica", 22 )
scoreText.x = centerX
scoreText.y = display.screenOriginY + 10
spawnIncrease() -- first call brings from 0 to 1 and starts timer
spwanBalloons()
end