I am currently trying to work out how I should go about shooting projectiles using Corona SDK. However, I don't know the best way to go about doing this. I am guessing that you have to spawn instances of the same object and apply force to them but I don't know the best way to do it or how I should handle each instance. I am still learning Lua and just need some guidance on how to do it, any help will be appreciated.
I want to be able to check if any of the bullets hit a sensor object ( I haven't implemented this but I know how to ) at the top of the screen and then destroy the bullet that hit the sensor but how do I check each instance and destroy them individually?
This is the basic structure that I have so far.
display.setStatusBar( display.HiddenStatusBar )
local physics = require( 'physics' )
physics.start()
local speed = -500
local contentW, contentH = display.contentWidth, display.contentHeight
-- Background
local bg = display.newRect( 0, 0, contentW, contentH )
bg.anchorX = 0
bg.anchorY = 0
bg:setFillColor( 0, 1, 1 )
-- Ground
local ground = display.newRect( 0, contentH - 50, contentW, 50 )
ground.anchorX = 0
ground.anchorY = 0
ground:setFillColor( 0, 0.8, 0 )
-- Hero
local hero = display.newRect( contentW / 2, contentH / 2, 40, 40 )
hero:setFillColor( 1, 0, 0 )
function shoot( event )
if ( event.phase == 'began' ) then
local projectile = display.newRect( hero.x, hero.y, 10, 30 )
physics.addBody( projectile, 'dynamic' )
projectile.gravityScale = 0
projectile.isBullet = true
projectile:setLinearVelocity( 0, -600 )
end
end
Runtime:addEventListener( 'touch', shoot )
This was suggested on the Corona forums by the Corona staff.
To remove each bullet that hits the sensor you need to give the projectile a 'type' of something along those lines. Note that you can use any word instead of 'type', but this is my preferred way.
projectile.type = 'bullet'
Then, you need to add an event listener to the sensor object that detects the collision, in this case it is an object called 'wall'. On collision you want to remove the other object that was in the collision ( the bullet ). You can do this like so.
local function wallCollision( event )
if event.phase == 'began' then
if event.other.type == 'bullet' then
display.remove( event.other )
event.other = nil
end
end
end
wall:addEventListener( 'collision', wallCollision )
'event.other' targets the other object involved in the collision event, in this case, the 'bullet'.
Not sure this is what you are after, but the strategy to handle evolving multiple objects which can be removed later as a result of collision is:
create the bullet display object, with a collision handler
in the collision handler, if object needs to be removed then use removeSelf; other changes may require delayed change as explained in Modifying Objects.
So in your shoot function you would add, after projectile:setLinearVelocity:
projectile.collision = function (event)
...
if remove then
self:removeSelf()
end
...
end
projectile:addEventListener( "collision", projectile)
This adds the handler to each bullet. You could instead add just one handler for the sensor, it would be similar code which you would put right after creating the sensor, except that you remove the event.other instead of self:
sensor.collision = function (event)
...
if remove then
event.other:removeSelf()
end
...
end
sensor:addEventListener( "collision", sensor)
Related
In my game (using Corona SDK) I want to spawn an enemy every 3 seconds. My spawnBlob creates only 1 blob at a time but four are appearing on screen at once every 3 seconds. I'm new at lua and corona and am having trouble tracing this code and figuring how things are being called four times when they shouldn't be. I am also having this problem with collision detection where i print the position where two objects collide. When two objects collide however, 4 lines of the print statement get printed and I don't know what is going on.
Is there an event.phase for this timer I should be utilizing similar to the began for touch events?
local allBlobs = {} -- global variable
function spawnBlob( event )
allBlobs[#allBlobs + 1] = display.newSprite ( mainGroup, mySheet3,
sequenceDataBlob)
local blob = allBlobs[#allBlobs]
physics.addBody( blob, { density=0.3, friction=0.6 })
blob.x = math.random(0, display.contentWidth)
blob.y = -80
blob.myName = "blob" .. #allBlobs
physics.addBody(blob, "dynamic", {density=0.1, bounce=0, friction=.2,
radius=128})
blob:play()
end
--scene:create( event ) contains mainGroup, spriteSheets and buttons
timer.performWithDelay( 3000, spawnBlob, 0) --in scene:show(event)
--scene:hide (event ) is empty
--scene:destroy ( event ) is empty
scene:addEventListener("create", scene)
scene:addEventListener("show", scene)
scene:addEventListener("hide", scene)
scene:addEventListener("destroy", scene)
return scene
In the scene:show function you can use
local phase = event.phase
if (phase == "will") then
--call your listeners
elseif (phase == "did") then
end
I am using this code to move the monkey where i touch the screen. it works alright if i touch the screen one at a time but when i try to constantly touch the screen on the left and right side it skips frames and instead transport to the area on the screen where i touched previously
local function moveMonkey(event)
if event.phase == "began" then
speed = 1500 / screenWidth * (mAbs(monkey.x - event.x))
transition.to ( monkey, {time = speed, x = event.x} )
end
end
You should cancel the previous transition before starting a new one (on same parameters). Do this by saving the return value of transition.to and checking, before creating new transition, whether the variable is defined, if yes then call transition.cancel() on it:
local trans = nil
local function moveMonkey(event)
if event.phase == "began" then
speed = 1500 / screenWidth * (mAbs(monkey.x - event.x))
if trans ~= nil then
transition.cancel(trans)
end
trans = transition.to ( monkey, {time = speed, x = event.x} )
end
end
There are other ways of managing transitions, see . For instance you might be able to tag the transition and cancel it, so no need to keep a reference to it (but I can't test right now, don't know if transition.cancel on a non-existent tag will cause problem -- my guess is it will simply be ignored), but the tag name should be rather unique:
local function moveMonkey(event)
if event.phase == "began" then
speed = 1500 / screenWidth * (mAbs(monkey.x - event.x))
transition.cancel("monkey_trans")
transition.to ( monkey, {time = speed, x = event.x, tag="monkey_trans"} )
end
end
Circles of variable sizes will be drawn on blank.png (800 X 800) on touch and in run time. On touch, the coordinates (the positions of x-axis and y-axis coordinates in runtime on touch) will be stored in two variables myCoordx and myCoordy in began event. When a user moves his/her fingers on screen, circle will be drawn based on calculated radius and coordinates. Now the error keeps on appearing. Please help me to debug this code.
Runtime error
d:\corona projects\enterframeevent\main.lua:14: attempt to index global 'drawCircle' (a nil value)
stack traceback:
d:\corona projects\enterframeevent\main.lua:14: in main chunk
This is my main.lua file.
local screen = display.newImage( "blank.png")
function drawCircle:touch(event)
if event.phase == "began" then
local myCoordx = event.x
local myCoordy = event.y
elseif event.phase == "moved" then
local rad = (event.x - myCoordx) ^ 2
local myCircle = display.newCircle(event.x, event.y, rad )
myCircle:setFillColor( 1, 0, 1 )
end
end
Runtime:addEventListener( "touch", drawCircle )
Apparently you try to add :touch method to drawCircle, but you don't define it anywhere. You should init it to something at very least an empty table - i.e. {} or use relevant Corona method to create it.
As per my comment, the code posted can't write, or the error message is wrong. I'll assume the error is wrong, because the statement function drawCircle:touch(event) on line 3 attempts to add a method called touch(self) to a drawCircle table; yet the code does not create this table anywhere. You are either missing drawCircle = display.newSomething..., OR you could simply use a function not a method:
function touch(event)
...
end
Runtime:addEventListener( "touch", touch)
The latter works only because your touch function doesn't use the keyword self, which is an implicitly created variable for methods.
I think you could go with something like this:
-- I think you should define these outside the function
-- since they'll be out of scope in the "moved" phase
local myCoordx = 0
local myCoordy = 0
-- Moved this declaration outside the function
-- so it can be reused, and removed
local myCircle
function onTouch(event)
if event.phase == "began" then
myCoordx = event.x
myCoordy = event.y
elseif event.phase == "moved" then
local rad = (event.x - myCoordx) ^ 2
-- Keep in mind that this line will draw a new circle everytime you fall into the "moved" phase, keeping the old one
-- if i understood well, this is not what you want
-- local myCircle = display.newCircle(event.x, event.y, rad )
-- Try this instead, removing the old display object...
if myCircle then
myCircle:removeSelf()
myCircle = nil
end
-- ...and adding it again
myCircle = display.newCircle(event.x, event.y, rad )
myCircle:setFillColor( 1, 0, 1 )
end
end
-- Since "drawCircle" is not defined, point it directly to a function (in this case, "onTouch")
-- Runtime:addEventListener( "touch", drawCircle )
Runtime:addEventListener( "touch", onTouch )
Didn't have the chance to test the code on Simulator, i'll try it later and update the answer if needed.
UPDATE:
Tested it, and it works as i expected.
I'm all very new to this and everything in this project is just placeholders and scrap work. Some of you may recognize some of the graphics used from other Corona tutorials, but it's to help me get better, so don't be too judgemental. Here's a link to the download for the Corona Simulator: http://www.mediafire.com/download/6a78bsewgwsiyp2/GooMan.rar
Anyways, here's my issue. The jump button seems fine at first. If I hold it down, the character constantly jumps. And if I let go, he stops. If I simultaneously hold down jump while pushing one of the arrow buttons, he'll jump in that direction. However, it seems as though if I jump once, then I hit the jump button again RIGHT as the character is making contact with the ground, that he won't jump. There's a sudden lack of responsiveness. I literally have to pause slightly and wait for the character to fully hit the ground before I can make another successful jump. Why?
And here's all the relevant code for you to look at:
I have this at the beginning to help define my goo character's position:
local spriteInAir = false
local yspeed = 0
local oldypos = 0
local holding = false
I then created the jump button.
local bJump = display.newImage("Images/jumpbutton.png")
bJump.x = 445
bJump.y = 265
bJump.xScale = 0.78
bJump.yScale = 0.78
Followed up by creating an enterFrame Runtime Event which will update my goo character's position in the Y. Running the game at 60fps if that's relevant.
function checkSpeed()
yspeed = sprite.y - oldypos
oldypos = sprite.y
if yspeed == 0 then
spriteInAir = false
else
spriteInAir = true
end
end
Runtime:addEventListener( "enterFrame", checkSpeed )
Then the meat of it all. Created a function called hold which tells the game to not only make my goo character jump, but to keep my goo character constantly jumping as long as the bJump button is held down. Works perfectly. The "jumping" function is a touch event that listens for the hold function, and all of it is executed by the bJump button listening to the jumping function.
local function hold()
if holding and spriteInAir == false then
sprite:applyForce( 0, -8, sprite.x, sprite.y )
sprite:setLinearVelocity(0, -350)
spriteInAir = true
return true
end
end
local function jumping( event )
if event.phase == "began" then
display.getCurrentStage():setFocus( event.target )
event.target.isFocus = true
event.target.alpha = 0.6
Runtime:addEventListener( "enterFrame", hold )
holding = true
elseif event.target.isFocus then
if event.phase == "moved" then
elseif event.phase == "ended" then
holding = false
event.target.alpha = 1
Runtime:removeEventListener( "enterFrame", hold )
display.getCurrentStage():setFocus( nil )
event.target.isFocus = false
spriteInAir = false
return true
end
end
return true
end
bJump:addEventListener("touch",jumping)
Anyone who can help me identify this problem, I'd greatly appreciate it!
You're using a velocity check to detect if the character is on the ground. It can take a short while to set it back to zero after colliding with Box2D, so the better way to do it is to use a collision sensor:
sprite.grounded = 0 -- Use a number to detect if the sprite is on the ground; a Boolean fails if the sprite hits two ground tiles at once
function sprite:collision(event)
if "began" == event.phase then
-- If the other object is a ground object and the character is above it...
if event.other.isGround and self.contentBounds.yMax < event.other.contentBounds.yMin + 5 then
sprite.grounded = sprite.grounded + 1 -- ...register a ground collision
end
elseif "ended" == event.phase then
-- If the other object is a ground object and the character is above it...
if event.other.isGround and self.contentBounds.yMax < event.other.contentBounds.yMin + 5 then
sprite.grounded = sprite.grounded - 1 -- ...unregister a ground collision
end
end
end
sprite:addEventListener("collision")
Then, in your jump function, just check to see if sprite.grounded > 0. If it is, the player is grounded.
I am attempting to add/remove objects from the physics engine (addBody() and removeBody()) in an app I am working on. The app I am working on is modular so the issue is in one of two files.
The objects file (TransmitterObject) or the main file (main):
This is the relevant code for both:
main.lua
local physics = require("physics")
physics.start()
physics.setGravity(0,0)
physics.setDrawMode( "debug" )
local TransmitterObject = require("TransmitterObject")
function updateGame(event)
if(ITERATIONS % 100 == 0) then
tran1:activate() --create new physics object here
end
ITERATIONS = ITERATIONS + 1
--print(ITERATIONS)
end
Runtime:addEventListener("enterFrame", updateGame)
TransmitterObject.lua
function transmitter.new(props) --constructor
Transmitter =
{
x = props.x,
y = props.y,
receivers = props.receivers
}
return setmetatable( Transmitter, transmitter_mt )
end
function transmitter:activate()
local group = math.random(1, #self.receivers)
local receiver = math.random(1,#self.receivers[group])
local x , y = self.receivers[group][receiver][1], self.receivers[group][receiver][2]
local d = math.sqrt(math.pow((self.x-x),2) + math.pow((self.y-y),2))
local dx = math.abs(self.x - x)
local angle = math.deg(math.acos(dx/d))
local beam = display.newRect(self.x,self.y, d, 10)
beam:setReferencePoint(display.TopLeftReferencePoint)
beam.rotation = 180 + angle
beam:setFillColor(0,255,0)
beam.alpha = 0
local function add(event)
physics.addBody(beam, "static")
end
local function delete(event)
physics.removeBody(beam)
end
transition.to( beam, { time=1000, alpha=1.0, onComplete=add } )
transition.to( beam, { time=1000, delay=2500, alpha=0, onComplete=delete})
end
Now let me try to describe the issue a little better. basically every 100th time that 'enterFrame' fires I tell the transmitter object (tran1) to call its function 'activate'
which then preforms some basic math to get coordinates. Then it creates a rectangle (beam) using the calculated information and sets some properties. That is all basic stuff. Next I tell it to transition from not visible (alpha = 0) to visible over the span of 1 second. When does it is to call the function 'add' which adds the object to the physics engine. Likewise with the next line where it removes the objects.
That being said, when i set physics.setDrawMode( "debug" ) the beam object appears as a static body, but does not accept collisions. Does anyone know why the above code would not accept collisions for the beam object?
Keep in mind I have other objects that do work properly within the physics engine.
Wow, I'm answering super late!
On collisions, modifying bodies aren't supported.
What I propose you is to create a new function,
local function addBody ( event )
physics.addBody(ball, "static")
end
and in your collision event you have to add this,
timer.performWithDelay(500, addBody)
The only thing that may cause some problems it's the delay, but as the collision doesn't take too much time it should be ok.
Sorry for this necroposting,
It's just to help other people that may have that problem,
Fannick