Follow up of this question, Storyboard with Ceramic Tile Engine, and with Collision Detection is still a mystery. Here is the code:
-- hide status bar
display.setStatusBar(display.HiddenStatusBar)
local storyboard = require("storyboard")
--Set up the physics world
local physics = require("physics")
physics.start()
physics.setGravity(0, 0)
physics.setDrawMode('hybrid')
local scene = storyboard.newScene()
local widget = require("widget")
-- Add Hero to Physics
local hero = display.newImage("images/man.png")
hero.x = 40
hero.y = 80
local heroCollisionFilter = { categoryBits = 4, maskBits = 2 }
local heroBody = { filter=heroCollisionFilter, isSensor=true }
physics.addBody(hero, "dynamic", heroBody)
function scene:createScene( event )
local group = self.view
local ceramic = require("Ceramic")
ceramic.showPrints = false
local map = ceramic.buildMap("maps/map.lua")
-- collisionLayer = map.layer['Collision']
-- collisionLayer.ccName = "map"
-- physics.addBody(collisionLayer, "static", { friction=0.5, bounce=0.3 } )
map.y = 0
map.setCameraDamping(10)
map.layer['World']:insert(hero)
end
function onGlobalCollision(event)
if(event.phase == "began") then
print( "Global report: " .. event.object1.ccName .. " & " .. event.object2.ccName .. " collision began" )
elseif(event.phase == "ended") then
print( "Global report: " .. event.object1.ccName .. " & " .. event.object2.ccName .. " collision ended" )
end
print( "**** " .. event.element1 .. " -- " .. event.element2 )
end
Runtime:addEventListener("collision", onGlobalCollision)
scene:addEventListener( "createScene", scene )
return scene
And the screenshot looks like:
However, collision never triggers, as the print message does not appear in Terminal at all.
I'm using:
Corona SDK
Ceramic Tile Engine
Corona module: storyboard, physics
How can I enable Collision Detection ? Are the parameters correct ?
Edit 2013/10/27
The Tiled map settings are as follow:
When running in Mac OS X, the collision does not happen ( only the hybrid layer changes color ).
When running in Windows 7, the code crashes on this line:
ceramic.buildMap("maps/map.lua")
with error:
attempt to call global 'reversePolygon' (a nil value) in Ceramic.lua:
617
After I comment out the following lines, the error is gone:
collisionLayer = map.layer['Collision']
collisionLayer.ccName = "map"
physics.addBody(collisionLayer, "static", { friction=0.5, bounce=0.3 } )
but the collision function does not get called.
Box2D collision detection is specified through the properties of a layer, tile, or object in an object layer. Ceramic adds physics automatically if the physics:enabled property is set to true.
Physics parameters are also set within properties. This:
physics.addBody(myObject, {friction = 0.5, bounce = 0.1})
Would correspond, in Tiled's properties, to this:
physics:friction = 0.5
physics:bounce = 0.1
For future people who are stuck in Collision Detection in Corona SDK with Tiled and Ceramic Tile Engine
In further testing, I found out that the issue of collision event not firing is I used a wrong set of collision events. The working collision events are :
local function onLocalCollision(self, event)
print("collision")
if event.phase == "began" then
print("Collision began")
elseif event.phase == "ended" then
print("Collision ended")
end
end
function onGlobalCollision(event)
if(event.phase == "began") then
print( "Global report: " .. event.object1.ccName .. " & " .. event.object2.ccName .. " collision began" )
elseif(event.phase == "ended") then
print( "Global report: " .. event.object1.ccName .. " & " .. event.object2.ccName .. " collision ended" )
end
print( "**** " .. event.element1 .. " -- " .. event.element2 )
end
function onPostCollision(event)
print("postCollision")
end
-- Local Collision
hero.collision = onLocalCollision
hero:addEventListener("collision", hero)
-- Global Collision
Runtime:addEventListener("collision", onGlobalCollision)
Runtime:addEventListener("postCollision", onPostCollision)
and each collision object has to have a name ( the property name ccName , you can pick any name you want , but it has to be set in Tiled's object list ).
Also, I removed the categoryBits and maskBits, seems they make the collision detection invalid.
Points to note:
Collision layer does not have to add to scene by programming ( it will be added automatically )
Only 1 set of collision detection methods ( Local / Global ) is needed ( but 2 sets can be run in parallel )
Turn off hybrid display mode when not needed, for better performance
It doesn't matter what the Layer format is ( Base64 / CSV works fine )
Remember to add physics:enabled in Collision Layer properties ( physics:friction and physics:bounce are optional , as per #CalebP's comment )
Related
I had a problem creating a loading scene in between scenes, the solution I got was to use an overlay, I've been trying this for days but for some reason I can't get it to work. I have two scenes, one that shows a list of items, and another that shows a page of text based on which row was clicked. The problem now is the first scene, that shows the list, downloads images to display and of course this takes a while so whenever i transition into that screen, the app appears to be frozen while everything loads up.
I've found out that it does actually go into the overlay scene, since the print statements i put in it print out, but it just doesn't display anything and when i removed the code that hides the overlay after, it still appears to hang, it goes into the overlay scene, doesn't display anything and then renders the table before finally displaying the overlay so instead of showing an overlay, having everything load up beneath, and then hiding the overlay, it appears to be frozen in the current scene, loads up everything beneath, shows overlay and then hides overlay right after.
My code for the three scenes are below, the one I'm posting now is the only one that actually worked once and then never again, it showed the overlay while everything loaded like I wanted but for some reason, it never worked again after that one time. I'm getting really frustrated with it and no forum has given me a solution, I'd really really appreciate some help. Thanks!
ItemListPage.lua
local composer = require ( "composer" )
local widget = require( "widget" )
local json = require( "json" )
-- Load the relevant LuaSocket modules
local http = require( "socket.http" )
local ltn12 = require( "ltn12" )
local scene = composer.newScene()
--NavigationBar elements initiated
--Removed for readability
--image handler
local function networkListener( event )
if ( event.isError ) then
print ( "Network error - download failed" )
end
print ( "event.response.fullPath: ", event.response.fullPath )
print ( "event.response.filename: ", event.response.filename )
print ( "event.response.baseDirectory: ", event.response.baseDirectory )
end
local function onRowRender( event )
-- Get reference to the row group
local row = event.row
local params=event.row.params
local itemRow=3;
-- Cache the row "contentWidth" and "contentHeight" because the row bounds can change as children objects are added
local rowHeight = row.contentHeight
local rowWidth = row.contentWidth
row.rowTitle = display.newText( row, params.topic, 0, 0, nil, 14 )
row.rowTitle:setFillColor( 0 )
row.rowTitle.anchorX = 0
row.rowTitle.x = 0
row.rowTitle.y = (rowHeight/2) * 0.5
--Other elements removed for readabilty (it's all just text objects)
--Download Image
--params referring to items[i]
local imagelink =params.imagelink
-- Create local file for saving data
local path = system.pathForFile( params.imagename, system.TemporaryDirectory )
myFile = io.open( path, "w+b" )
-- Request remote file and save data to local file
http.request{
url = imagelink,
sink = ltn12.sink.file( myFile )
}
row.Image = display.newImageRect(row, params.imagename, system.TemporaryDirectory, 25, 25)
row.Image.x = 20
row.Image.y = (rowHeight/2) * 1.5
row:insert( row.rowTitle )
row:insert( row.Image )
end
local function onRowTouch( event )
local row = event.target
local params=event.target.params
composer.removeScene(composer.getSceneName("current"))
composer.gotoScene( "itempage" , {params=params})
end
function scene:create( event )
local sceneGroup = self.view
end
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if phase == "will" then
-- Called when the scene is still off screen and is about to move on screen
--overlay
composer.showOverlay( "loading", { isModal = true })
elseif phase == "did" then
--Table stuff
local scrollBarOptions = {
sheet = scrollBarSheet, -- Reference to the image sheet
topFrame = 1, -- Number of the "top" frame
middleFrame = 2, -- Number of the "middle" frame
bottomFrame = 3 -- Number of the "bottom" frame
}
-- Table
local tableView = widget.newTableView(
{
left = 0,
top = navBar.height,
height = display.contentHeight-navBar.height,
width = display.contentWidth,
onRowRender = onRowRender,
onRowTouch = onRowTouch,
listener = scrollListener
}
)
--json work
local filename = system.pathForFile( "items.json", system.ResourceDirectory )
local decoded, pos, msg = json.decodeFile( filename )
if not decoded then
print( "Decode failed at "..tostring(pos)..": "..tostring(msg) )
else
print( "File successfully decoded!" )
end
local items=decoded.items
-- create a white background to fill screen
local background = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight )
background:setFillColor( 1 ) -- white
sceneGroup:insert( background )
-- Insert rows
for i = 1, #items do
-- Insert a row into the tableView
print( "Adding a row!" )
tableView:insertRow{
rowHeight = 100,
rowColor = { default={ 0.8, 0.8, 0.8, 0.8 } },
lineColor = { 1, 0, 0 },
params=items[i]
}
end
sceneGroup:insert( tableView )
composer.hideOverlay( "fade", 100 )
end
end
-- other functions and elements unused and removed for readability
loading.lua
local composer = require ( "composer" )
local widget = require( "widget" )
local json = require( "json" )
local scene = composer.newScene()
-- Create the widget
function scene:create( event )
local sceneGroup = self.view
local background = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight )
background:setFillColor( 1 ) -- white
local text = display.newText( "Loading scene", 0, 0, nil, 14 )
text:setFillColor( 0 )
text.anchorX = display.contentCenterX
text.x = display.contentCenterX
text.y = display.contentCenterY
sceneGroup:insert( background )
sceneGroup:insert( text )
print ( "In loading create")
end
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if phase == "will" then
elseif phase == "did" then
print ( "In loading show")
end
end
-- other functions and elements unused and removed for readability
ItemDisplayPage.lua
local composer = require ( "composer" )
local widget = require( "widget" )
local json = require( "json" )
local scene = composer.newScene()
--NavigationBar elements initiated
--This creates the "back button", when clicked it returns to the previous scene, in this case "itemListPage"
--it takes, no parameters
local function handleLeftButton( event )
if ( event.phase == "ended" ) then
composer.removeScene(composer.getSceneName("current"))
composer.gotoScene(composer.getSceneName("previous"))
end
return true
end
--Remaining navbar elements removed for readability
function scene:create( event )
local sceneGroup = self.view
local params=event.params
-- create a white background to fill screen
local background = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight )
background:setFillColor( 1 ) -- white
--creating header bar
local bar = display.newRect( navBar.height + (headerBarHeight*0.5), display.contentCenterY, display.contentWidth, headerBarHeight )
bar:setFillColor( 1 )
-- create stuff
local title = display.newText(params.topic, 0, 0, nil, 14 )
title:setFillColor( 0 )
title.anchorX = 0
title.x = margin
title.y = ((2*headerBarHeight/2) * 0.5)+navBar.height
local Image = display.newImageRect(params.imagename, system.TemporaryDirectory, 25, 25)
Image.x = 50
Image.y = display.contentCenterY
-- all objects must be added to group (e.g. self.view)
sceneGroup:insert( background )
sceneGroup:insert( title )
sceneGroup:insert( Image)
end
-- other functions and elements unused and removed for readability
I suggest you do not use scene.show event for loading.
Use timer.performWithDelay to load all data:
--in scene:show
elseif phase == "did" then
timer.performWithDelay(0, function()
local scrollBarOptions = {
--put your code here
composer.hideOverlay( "fade", 100 )
end)
Your current code didn't show overlay because engine waits for scene:show event before rendering anything. So rendering of overlay and images occured after all images are loaded.
In my code timer.performWithDelay doesn't block scene:show execution, so you will see overlay rendered before loading images
I am doing a game in corona SDK, but I have this little problem.
I have a menu with a button. If I press it, it sends me to the first level of my game.
When I pass the final level, the game return me to the menu. Bur, if I start playing the first again, my images doesn´t appear.
The images are balls, and to pass the level, you have to eliminate all the balls. To do this, I use:
ball:removeSlef()
ball = nil
But, I don´t think that this is the problem, because I eliminate this lines, and it doesn´t work.
The images are create in scene:createScene function, and insert in the Group.
I short the code of the first level to be understood.
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
local physics = require "physics"
physics.start(); physics.pause()
physics.setGravity( 0, 0 )
local cont = 0
local bur = {}
function eliminar1( event )
if (cont == 0) and (event.phase == "began") then
event.target:removeSelf()
bur[1] = nil
cont = cont + 1
end
end
function eliminar2( event )
if (cont == 1) and (event.phase == "began") then
bur[2]:removeSelf()
bur[2] = nil
cont = cont + 1
end
end
function eliminar3( event )
if (cont == 2) and (event.phase == "began") then
bur[3]:removeSelf()
bur[3] = nil
storyboard.gotoScene( "levels.1.level2" )
end
end
function scene:createScene ( event )
local screenGroup = self.view
for i = 1,3 do
bur[i] = display.newImage("irudiak/"..i..".png")
bur[i]:translate(math.random(0,280), math.random(0,400) )
physics.addBody( bur[i], {bounce = 0.3 } )
bur[i]:setLinearVelocity(math.random(-50,50), math.random(-50,50) )
screenGroup:insert(bur[i])
end
bur[1]:addEventListener("touch", eliminar1)
bur[2]:addEventListener("touch", eliminar2)
bur[3]:addEventListener("touch", eliminar3)
end
function scene:enterScene( event )
local screenGroup = self.view
physics.start()
end
function scene:exitScene( event )
local screenGroup = self.view
physics.stop()
end
function scene:destroyScene( event )
local screenGroup = self.view
package.loaded[physics] = nil
physics = nil
end
return scene
createScene is ran only first time when you gotoScene. Every next time only willEnterScene and enterScene are played. To play createScene again you have to remove it (storyboard.removeScene() I guess). Or you can move some stuff you need to willEnterScene. For more detailed info you can watch this: http://www.coronalabs.com/blog/2013/08/20/tutorial-reloading-storyboard-scenes/
I have the following scene:
I want to move the ball using gravity so I wrote the following:
function onTilt( event )
deltaDegreesXText.text = "xG: " .. (9.8*event.xGravity)
deltaDegreesYText.text = "yG: " .. (-9.8*event.yGravity)
if(ball.isAwake==true)then
fallAsleepText.text = "True "
elseif(ball.isAwake==false)then
fallAsleepText.text = "False "
end
physics.setGravity( ( 9.8 * event.xGravity ), ( -9.8 * event.yGravity ) )
end
The problem is that after some seconds the ball object fall sleep and do not move any more. In the begging of the code I have the following:
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
system.setIdleTimer( false )
local physics = require "physics"
local physicsData = (require "myphysics").physicsData(1.0)
physics.start( true)
Any idea whats going wrong? Why the physics.start( noSleep ) does not work or how I can I stop the ball from falling to sleep?
Mmh that's weird. Have you tried disabling sleeping in the body definitions?
body.isSleepingAllowed = false;
Yes, I tried that and at the begining it didn't work. Finally the problem was that I wrote:
ball.isSleepingAllowed = false
physics.addBody (ball, physicsData:get("ball"))
but the correct is:
physics.addBody (ball, physicsData:get("ball"))
ball.isSleepingAllowed = false
I should first add physics to the ball and then body.isSleepingAllowed = false; the opposite didn't work.
I'm using director , to move from one scene to another. I have a problem that the buttons and text fields from intor still in the top of screen when moving to any scene.
How to remove items (text fields, btns from intro.lua screen) before move to next scene?
enter code here
-- into.lua
module(..., package.seeall)
function new()
--
-- Project: NativeKeyboard2
--
local widget = require( "widget" )
require("hijacks")
local tHeight -- forward reference
-------------------------------------------
-- General event handler for fields
-------------------------------------------
-- You could also assign different handlers for each textfield
local function fieldHandler( textField )
return function( event )
if ( "began" == event.phase ) then
-- This is the "keyboard has appeared" event
-- In some cases you may want to adjust the interface when the keyboard appears.
elseif ( "ended" == event.phase ) then
-- This event is called when the user stops editing a field: for example, when they touch a different field
elseif ( "editing" == event.phase ) then
elseif ( "submitted" == event.phase ) then
-- This event occurs when the user presses the "return" key (if available) on the onscreen keyboard
print( textField().text )
-- Hide keyboard
native.setKeyboardFocus( nil )
end
end
end
-- Predefine local objects for use later
local nameField, phoneField
local fields = display.newGroup()
-------------------------------------------
-- *** Create native input textfields ***
-------------------------------------------
-- Note: currently this feature works in device builds or Xcode simulator builds only (also works on Corona Mac Simulator)
local isAndroid = "Android" == system.getInfo("platformName")
local inputFontSize = 18
local inputFontHeight = 30
tHeight = 30
if isAndroid then
-- Android text fields have more chrome. It's either make them bigger, or make the font smaller.
-- We'll do both
inputFontSize = 14
inputFontHeight = 42
tHeight = 40
end
nameField = native.newTextField( 40, 120, 200, tHeight )
nameField.font = native.newFont( native.systemFontBold, inputFontSize )
nameField:addEventListener( "userInput", fieldHandler( function() return nameField end ) )
phoneField = native.newTextField( 40, 160, 200, tHeight )
phoneField.font = native.newFont( native.systemFontBold, inputFontSize )
phoneField.inputType = "phone"
phoneField:addEventListener( "userInput", fieldHandler( function() return phoneField end ) )
-- Add fields to our new group
fields:insert(nameField)
fields:insert(phoneField)
-------------------------------------------
-- *** Add field labels ***
-------------------------------------------
local defaultLabel = display.newText( "الاسم", 250, 120, native.systemFont, 18 )
defaultLabel:setTextColor( 255, 0, 0 )
local defaultLabel = display.newText( "رقم الجوال", 250, 160, native.systemFont, 18 )
defaultLabel:setTextColor( 255, 0, 0 )
-- -------------------------------------------
-- -- Create a Background touch event
-- -------------------------------------------
local listener = function( event )
-- Hide keyboard
print("tap pressed")
native.setKeyboardFocus( nil )
return true
end
-- Determine if running on Corona Simulator
--
local isSimulator = "simulator" == system.getInfo("environment")
if system.getInfo( "platformName" ) == "Mac OS X" then isSimulator = false; end
-- Native Text Fields not supported on Simulator
--
if isSimulator then
msg = display.newText( "الرجاء ادخال اسمك ورقم جوالك", 0, 280, native.systemFontBold, 12 )
msg.x = display.contentWidth/2 -- center title
msg:setTextColor( 255,0,0 )
end
-- -- Add listener to background for user "tap"
-- bkgd:addEventListener( "tap", listener )
-- display.remove( obj )
-- obj = nil
local introGroup = display.newGroup();
local background = display.newImage("graphics/intro_background.png")
local begin = display.newImage("graphics/begin_button.png")
begin.x = 160;
begin.y = 400;
begin.scene = "menu";
introGroup:insert(background);
introGroup:insert(begin);
begin:addEventListener("touch", changeScene)
return introGroup;
end
Corona provides a very good feature that is "Storyboard".I am giving you a brief explanation, Try this -
Storyboard - It is a scene (e.g. "screens" or "views") management library that provides developers an easy way to create and transition between scene modules in a Corona SDK app.
Syntax -
local storyboard = require "storyboard"
Example -
local scene1 = storyboard.newScene( "name of the Scene" )
Here are the different events used in the Storyboard -
1- Create Scene -
-- Called when the scene's view does not exist:
function scene:createScene( event )
local group = self.view
end
2- Enter Scene -
-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
local group = self.view
end
3- Exit Scene -
-- Called when scene is about to move offscreen:
function scene:exitScene( event )
local group = self.view
end
4 - Destroy Scene -
-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
local group = self.view
end
It will help you.
Hi i am trying to get this collision filter to work but i am a little bit stuck...
local bad1CollisionFilter1 = { categoryBits = 1, maskBits = 3 }
if ( temp.imgpath == "BCloud1.png" ) then
physics.addBody( randomStar, { density=2.9, friction=0.5, bounce=0.3, radius=16, filter=bad1CollisionFilter1 } )
end
local collision = function( bad1CollisionFilter1 )
if bad1CollisionFilter1.phase == 'began' then
print("Hello i am CollisionFilter1")
end
end
Any help to get this working is appreciated!
By looking at the Corona API for collision and EventListener; you need to add an even listener with the body. Add the following statement to your script.
Runtime:addEventListener( "collision", collision )
where the first argument states that it is a collision event listener and the second argument is the function name; which in your case is collision.