while constantly moving character, it skips frames - lua

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

Related

How can I handle collisions of multiple blocks as if they were a single entity?

I'm creating a 2D platform game using Corona SDK and I'm stuck with collisions.
Basically there is a character that runs over this ground made of blocks. This is because sometimes there can be holes in the ground.
The game is an endless one, so as the character moves forward new blocks (and holes) are dynamically added - and also removed if they goes off screen.
It works nicely but this approach works against the collision system, let me explain how.
Now that I have the ground in place I want the character to jump but only if it is touching the ground, to avoid jumping while in air.
Whenever a collision is detected between character and ground an event is fired - two times. The first time when the character is entering a ground block and the second time when the character leaves it. So when the character lands on the ground a isGround Boolean is set to true. And when - after a jump - it leaves it the flag is set to false. The problem is that every time it exits a block to enter another - walking along the ground without jumping - the flag get updated. This makes the jump based on the isGround flag less reliable. Sometimes it happens that you can't jump because isGround == false though the character is on the ground.
Ground block creation snippet
-- init() method set the sprite of the ground block and physic to that sprite
function GroundBlock:init()
self.sprite = display.newImageRect(self.path, self.width, self.height)
self.sprite.x = self.x
self.sprite.y = self.y
physics.addBody(self.sprite, 'static', {
density = 0,
friction = 0,
bounce = 0,
box = {
halfWidth = self.width / 2,
halfHeight = self.height / 2,
y = 16,
x = 0
}
})
local collisionObj = {
name = 'ground'
}
self._collision = collisionObj
self.sprite._collision = collisionObj
self.isShow = true
end
Ground placing GroundBlocks snippet
-- init() method initialize the ground with a fixed number of blocks
function Ground:init()
self.offsetX = 0
while self.offsetX < self.camera.borderRight * 2 do
self._createBlock(1)
end
self.lastCameraPos = self.camera.borderRight
end
-- update() is called once per frame
function Ground:update()
if (self.camera.borderRight - self.lastCameraPos > self._blockWidth) then
local rand = math.ceil(math.random() * 10) % 2
if self._skippedBlock >= 2 or rand == 0 then
self._createBlock(1)
self._skippedBlock = 0
else
self._createBlock(0)
self._skippedBlock = self._skippedBlock + 1
end
self.lastCameraPos = self.camera.borderRight
end
for i, block in ipairs(self.blocks) do
if block.sprite.x < self.camera.borderLeft - block.width then
table.remove(self.blocks, i)
self.camera:remove(block.sprite)
block:delete()
end
end
end
Collision detection snippet
function Character:collision(event)
if ( event.phase == "began" ) then
if event.other._collision.name == "ground" then
self.isGround = true
end
elseif ( event.phase == "ended" ) then
if event.other._collision.name == "ground" then
self.isGround = false
print('nope')
end
end
end
A solution would be to make a ground as a single imgRect but how to make holes in it?
You could simplify your code and prevent this issue from ever occurring by tracking if the character can jump instead of tracking if the character is on the ground.
For instance,
function jump( event )
if event.phase == "began" then
if canJump then
canJump = false
-- your code that makes the player jump
end
end
end
You probably use touches to determine whether the player character jumps, right? This way, you'll trigger the jump when the touch starts as long as the character has not already jumped.
You could then reset this value in your collision function by editing it slightly:
function Character:collision(event)
if event.phase == "began" then
if event.other._collision.name == "ground" then
canJump = true
end
end
end
This way, the character's ability to jump is determined by whether or not the player has pressed jump already and if the character has hit the ground since the last jump.
This kind of approach also gives you the ability to pivot towards implementing mechanics like double jump. If instead of using a boolean canJump variable you chose to use a number variable, e.g. jumpsLeft, you could reduce the number of jumps left every time the character jumps and only let the character jump if jumpsLeft is larger than 0. Then you'd simply reset the value back to 1 (or whatever you'd want upon hitting the ground).

Can someone test my Corona project and identify the issue with my jump function?

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.

Drag physic object in corona sdk

I'm try to drag a dynamic body with gravity = 0,0 in my scene, I have a square with body type dynamic, and an image with body type static, but when drag the square over the image this have a little force but can exceed the image and pass to the other side like the images:
This is my code to drag the square:
local function dragBody( event )
local body = event.target
local phase = event.phase
local stage = display.getCurrentStage()
if "began" == phase then
stage:setFocus( body, event.id )
body.isFocus = true
body.tempJoint = physics.newJoint( "touch", body, event.x, event.y )
elseif body.isFocus then
if "moved" == phase then
body.tempJoint:setTarget( event.x, event.y )
elseif "ended" == phase or "cancelled" == phase then
stage:setFocus( body, nil )
body.isFocus = false
body.tempJoint:removeSelf()
end
end
return true
end
And this is the code to create the objects:
function scene:createScene( event )
local group = self.view
my_square = display.newImage("square.png")
my_square.x = 60
my_square.y = 60
physics.addBody(my_square, "dynamic" )
group:insert(my_square)
floor = display.newImage("piso.png")
floor.x = 160
floor.y = 240
physics.addBody(floor, "static" )
group:insert(floor)
end
thanks for help.
First, I recommed you to try:
physics.setContinuous( false )
If you did that already:
There are 3 different physics type in Physics2D engine. For drag purposes you can use "Kinematic" object type. But if its an obligation to use a Dynamic object as a dragable object, there may be bugs in collisions. But if your static object will be same every time, you can control it in drag function.
I have implemented a little mobile game using same thing that you want to achieve. Here is the link:
https://itunes.apple.com/tr/app/bricks-world/id602065172?mt=8
If you think that you want something similar in this game, just leave a comment^^ I can help further.
P.S : In the game, the controller paddle is dynamic and walls around the screen are static.
Another Solution:
local lastX, lastY
local function dragBody( event )
local body = event.target
local phase = event.phase
local stage = display.getCurrentStage()
if "began" == phase then
stage:setFocus( body, event.id )
body.isFocus = true
lastX, lastY = body.x, body.y
elseif body.isFocus then
if "moved" == phase then
-- You can change 1's with another value.
if(event.x > lastX) body.x = body.x + 1
else body.x = body.x - 1
if(event.y > lastY) body.y = body.y + 1
else body.y = body.y - 1
lastX, lastY = body.x, body.y
elseif "ended" == phase or "cancelled" == phase then
stage:setFocus( body, nil )
body.isFocus = false
end
end
return true
end
When you manually move an object you are doing so out of control of Physics and basically you can force the object to move past the static body.
What you can do is setup collision detection that will give you an event when you move the square that will tell you when to stop moving. Of course if you don't honor that in your move code you can keep moving your object.
Try putting
physics.setContinuous( false )
"By default, Box2D performs continuous collision detection, which prevents objects from "tunneling." If it were turned off, an object that moves quickly enough could potentially pass through a thin wall."

Update the target coordinates while transitioning

I am making a game in corona and I am facing a problem. I have a circle on screen and I want it to follow the touch coordinates continuously. I am using transition.to function to do so but the thing is, whenever this function get a coordinate, it completes the transition even if the coordinates are updated during the transition.
if event.phase == "began" or event.phase == "moved" then
follow = true
touchX = event.x; touchY = event.y
elseif event.phase == "ended" then
follow = false
end
And in another function, I am doing this
if follow == true then
transition.to(circle, {time = 500, x = touchX, y = touchY, transition = easing.inOutQuad})
end
The code works fine for simple touch but I want the circle to follow the touch even when it's moving.
There are some examples which may solve your problem.
Refer:
1) Flight Path posted by carlos in corona Community.
2) Move Object through a path by renvis
Sample:
local circle = display.newCircle(10,10,20)
circle.x = 160
circle.y = 160
local olderTransition
local function moveCircle(e)
if olderTransition ~= nil then
transition.cancel( olderTransition )
end
olderTransition = transition.to(circle,{time=100,x=e.x,y=e.y})
end
Runtime:addEventListener("touch",moveCircle)
Keep Coding.......... :)
You cant add a new transition to an object, which already in a transition. Thats why you should cancel the older transition first. You can try :
local olderTransition -- This should be visible outside of your function
local function blabla()
if follow == true then
if olderTransition ~= nil then
transition.cancel( olderTransition )
end
olderTransition = transition.to(circle, {time = 500, x = touchX, y = touchY, transition = easing.inOutQuad, onComplete = function() olderTransition = nil end })
end
end
Btw If you want to drag and drop objects, transitions are bad in performance way

Corona SDK - change a variable for the duration of a drag event

I have a "seed" object which has an instance method seed:fall() which is called by my update function (which runs every frame). I've got a "touch" event listener on it so the user can drag it around. When it's being dragged, however, it's still trying to fall, which makes the drag interaction glitchy.
I've added an instance variable to my seed "class" called seed.falling. The fall() function now checks that seed.falling is true before moving the seed down the screen. The next step is to set seed.falling to false when the drag starts, and then set it back to true when the drag stops. I can't figure out this last part though.
Any ideas anyone? Is there a "stop dragging" event i could set a listener for, to switch seed.falling back on? Is there a nicer way of achieving what i want?
physics.start()
physics.setGravity(0,1)
local dd = display.newRect(400,100,200,200)
physics.addBody(dd,"dynamic")
dd:addEventListener("touch", function(event)
if event.phase == "began" then
dd.bodyType = "static"
elseif event.phase == "moved" then
dd.x,dd.y = event.x,event.y
elseif event.phase == "ended" then
dd.bodyType = "dynamic"
end
end)
I think this case is what you want?
Just for the record, here's how i solved this.
Basically i have an attribute "seed.falling" which the seed:fall method checks before moving the seed. And i set that attribute to false if we're not at the "ended" phase of the drag event, which stops the seed falling.
function Seed:new(x,y)
print("Seed:new, x = " .. (x or nil) .. ", y = " .. (y or nil) )
local seed = display.newImage("seed_icon.png")
seed.x = x
seed.y = y
seed.name = 'seed'
seed.falling = true
function seed:fall()
if(self.falling) then
self.y = self.y + 1
end
end
function seed:drag(event)
seed.x = event.x
seed.y = event.y
if(event.phase == "ended") then
seed.falling = true
else
seed.falling = false
end
end
seed:addEventListener("touch", drag)
return seed
end
function drag(event)
seed = event.target
seed:drag(event)
end
It's not a very good solution i think as it leaves the seed stranded on the screen sometimes - possibly when you drag a seed over another falling seed.

Resources