Confused about event.type Lua - lua

I'm working from a book and learning about orientation. I'm very confused about the code event.type.
Here's the code from the book:
portrait = display.newText ("Portrait", display.contentWidth / 2,display.contentHeight / 2, nil,20)
portrait: setFillColor ( 1,1,1 )
portrait.alpha = 1
landscape = display.newText ("Landscape", display.contentWidth/ 2, display.contentHeight /2, nil, 20)
landscape: setFillColor (1,1,1)
landscape.alpha = 0
local function onOrientationChange (event)
if (event.type == 'landscapeRight' or event.type == 'landscapeLeft') then
local newAngle = landscape.rotation - event.delta
transition.to (landscape, {time = 1500, rotation = newAngle})
transition.to (portrait, {rotation = newAngle})
portrait.alpha = 0
landscape.alpha = 1
else
local newAngle = portrait.rotation - event.delta
transition.to (portrait, {time = 150, rotation = newAngle})
transition.to (landscape, {rotation = newAngle})
portrait.alpha = 1
landscape.alpha = 0
end
end
Seems like the whole orientation change function works around event.type. I don't understand what it is, and I don't understand what it's equal to (==). Further when I change the string(in this case 'landscapeRight' and 'landscapeLeft') that it's equal too. It will take any value and still function okay. I'm completely confused on how it works, please explain event.type.

It is a common Lua idiom to use strings such 'landscapeRight' as enum literals.
For orientation events, event.type holds the new device orientation.
In the code snippet you provided, you don't seem to be calling or otherwise making any reference to onOrientationChange after defining it. You should attach it to the Runtime object using
Runtime:addEventListener('orientation', onOrientationChange)

I hope you accept MBlanc's answer, I'm just going to expand here: the orientation event.type is a value that is one of several strings, as indicated on that link in MBlanc's post. Event.type will never be anything other than those strings. So what you are doing by changing the comparison to strings that can never match event.type is ending up in the "else" branch all the time, as though your device was never oriented in landscape:
local function onOrientationChange (event)
if (event.type == 'blabla1' or event.type == 'blabla2') then
...do stuff -- but will never get run because event.type can never have those values
else -- so your program always ends up here:
...do other stuff...
end
end
This will make it appear as though program is running fine except that when your device really is in orientation landscapeRight or left the program will execute the "else" block instead of the block it should have executed (first block).

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).

How do I implement recoil on a gun in LUA, 2D?

I'm trying to create a gun simulator type mobile application, and am currently trying to code a fire function. it's going pretty well so far, but me being a novice I'm having troubles figuring out what I can do to resolve my issue:
function pistolRecoil()
transition.to(pistol, {time = 30, rotation = -20 })
end
function revPistolRecoil()
transition.to(pistol, {time = 50, rotation = 20 })
end
--Fire the pistol--
function tapFirePistol (event)
--display flash
muzzleFlashP:toFront()
timer.performWithDelay(20, muzzleFlashPFunc())
pistolAmmo = pistolAmmo - 1
----timings
--sound(.wav),
audio.play(pFireSound)
--rotate,rotate
pistolRecoil()
print("1st", pistol.rotation)
pistolRecoil()
print("2nd", pistol.rotation)
revPistolRecoil()
----timings
if pistolAmmo <= 0 then fireButtonPistol:removeEventListener("tap",
tapFirePistol)
pistolAmmoCount.text = pistolAmmo
end
if pistolAmmo <= 0 then pistolAmmo = 0
pistolAmmoCount.text = pistolAmmo
end
pistolAmmoCount.text = pistolAmmo
end
fireButtonPistol:addEventListener("tap", tapFirePistol)
The problem I'm having is that the gun will recoil, but stay rotated, which is bad as I would like it to "fall" back down after a short period of time, and will not recoil any further even in the fire button is tapped while the pistol is still rotated (which I'm not bothered by)
Any help is greatly appreciated.
So, the recoil aspect is now working as I am now using timer.performWithDelay. I found out that the reason the timer.performWithDelay was not behaving the way I expected is because i was using parenthesis inside the performWithDelay parenthesis, which are not needed, and I also specified that it should be done once.
timer.performWithDelay(20, revPistolRecoil, 1)
I still call pistolRecoil as a function.
As a side note, the muzzleFlashP also now works when I apply the same changes.
Thanks for the help!

while constantly moving character, it skips frames

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

How to draw circles of variable sizes on touch using Lua

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.

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.

Resources