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

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!

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

3 point object moving is happening all at once

I am currently making a jumping game where there is an object at the left side of the screen, on a platform. Once the object successfully jumps and lands on the platform on the right, it does the following:
1) the platform on the right moves to the left
2) the platform on the left (that you JUST jumped off from) moves off-screen.
3) a new platform is supposed to appear at the right side of the screen, thus continuing the loop.
I have already made the functions where it allows the object to jump and show if the collision is successful or not. My problem is, the 3 things I have mentioned above happens, but it keeps on going and does not stop for the object to make the next jump. This makes the object go off screen as well, since the platforms keep moving towards the left. After debugging, I feel like the problem lies where the collision happens. This is because, once it touches the platform, the collision keeps on happening until the object is off of the platform. I was wondering if you guys can help me with this!
Here is part of my code that is relevant:
local onPlatform = false
local gameStarted = false
function playerCollision( self, event )
if ( event.phase == "began" ) then
--if hit bottom column, u get points
if event.target.type == "player" and event.other.type == "bottomColumn" then
print ("hit column")
onPlatform = true
else
--if hit anything else, gameOver
--composer.gotoScene( "restart" )
print ("hit ground")
end
end
end
function moveColumns()
for a = elements.numChildren,1,-1 do
--speed of columns moving
--if greater than -100, keep moving it to left
--puts platform at the right and stops
if (elements[a].x > display.contentWidth/1.1) then
elements[a].x = elements[a].x - 12
end
--moves platform to left after it successfully lands
if (onPlatform == true) then
if (elements[a].x > display.contentWidth/3 and elements[a].x < display.contentWidth/1.11) then
elements[a].x = elements[a].x - 12
end
end
--moves left platform to off-screen and deletes after it passes a certain X-axis
if (onPlatform == true) then
if(elements[a].x > -100 and elements[a].x < display.contentWidth/2.99) then
elements[a].x = elements[a].x - 12
--adds score if it goes past a certain X-axis at left side
elseif(elements[a].x < -100) then
mydata.score = mydata.score + 1
tb.text = mydata.score
elements[a].scoreAdded = true
elements:remove(elements[a])
elements[a] = nil
end
end
end
end
When are you calling moveColumns?? Could you just call it from within playerCollision and remove the onPlatform variable? Probably need a bit more code to help properly.

Corona SDK: Having Single and Double Tap Events on One Object

I'm trying to have a different amount of force applied to an object when the screen is tapped a different amount of times ( only once and twice ).
I'm not sure what I'm doing wrong. Here is the code:
local function moveUp(event)
if event.numTaps > 1 then
jumper:applyForce( 0, 250, jumper.x, jumper.y )
elseif event.numTaps < 1 then
jumper:applyForce( 0, 0, jumper.x, jumper.y )
else
jumper:applyForce( 0, 200, jumper.x, jumper.y )
end
end
-- start game
createPlayScreen( )
system.setTapDelay( 2 )
Runtime:addEventListener("tap", moveUp)
I've tried moving the Runtime:addEventListener into the function. I've also tried have the event.numTaps == 2 and event.numTaps == 1, but to no avail.
The issue is that the TapDelay refuses to wait for the second tap.
Any and all help is greatly appreciated
P.S. I have the seconds set to two for testing purposes, but once I find that this works, I will be lowering the time to like 0.3 or something
There are several issues with the strategy you are using. For one, it is not scalable (to more than two taps). But ok, maybe you are 100% sure you will never ever need more than 2 taps. The next problem is that event.numTaps can only be 1 or 2, yet your listener tests for < 1! This can never happen. The next problem is that when you tap twice, at least in the simulator (did not test on device) you get two events: one for the first tap with numTaps = 1, and another for 2nd tap with numTaps = 2. In other words, the Corona engine does not wait before emitting a one-tap event to know if a second tap event occurs within some time range. So you get two tap events for a two-tap event, there is no way of knowing in the handler whether you should "wait" to see if another tap might occur within an allowable delay to constitute a "two-tap" event instead.
What you will have to do is create your own N-tap event generator. Whenever a tap occurs, check if your timer has been started. If so, increase the tap count and reset the timer. If not, start the timer that expires some short delay later. If no other tap occurs in that delay, the count you have saved is your tap number. Reset the counter if your timer expires. I have created some functions that do this and I have put them all in a table "object":
local tapEvents = {
measureInterTapTime = false, -- set to true to measure how fast you can tap!
onTapHandler = nil, -- set this to your handler
-- implementation details
tapTimer = nil,
tapCounter = 0,
tapEventTime = 0,
doneTap = function(self, event)
self.tapTimer = nil
if self.onTapHandler then
self.onTapHandler(self.tapCounter)
end
self.tapCounter = 0
self.tapEventTime = 0
end,
-- end implementation details
tap = function(self, event)
self.tapCounter = self.tapCounter + 1
if self.tapTimer ~= nil then
timer.cancel(self.tapTimer)
self.tapTimer = nil
end
local delayMS = 250
self.tapTimer = timer.performWithDelay(delayMS, function(e) self:doneTap(e) end, 1)
-- check how much time between taps, for interest:
if self.measureInterTapTime then
if self.tapEventTime ~= 0 then
local interTapTime = system.getTimer() - self.tapEventTime
print("Time (ms) between taps:", interTapTime)
end
self.tapEventTime = system.getTimer()
end
end,
}
tapEvents.onTapHandler = function(tapCounter)
print(tapCounter .. "-tap event")
end
-- because tapEvents contains a 'tap' function, will get called automatically with self:
Runtime:addEventListener('tap', tapEvents)
This captures N-tap events without limit!! I have included a flag you can set to true if you want to print out the ms delay between single taps so you can determine what the optimum delay should be (you don't want delay to be too short or you might inadvertently break an N tap into two smaller events; you don't want it to be too long either, or user will have to noticeably wait to indicate "end of my multitap").
Tap events must be added to a display object, not to the Runtime.
If you have a display object jumper for example, use:
jumper:addEventListener("tap", moveUp)
More documentation here: http://docs.coronalabs.com/api/event/tap/index.html

Confused about event.type 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).

How to rotate an Image using Corona sdk with iPhone

I am a newbie to Corona. I have an image that is draggable over the screen. Now i want to apply rotation to that image object.
The code I currently have is:
myAnim1 = movieclip.newAnim{"ICQ.png"}
--foreground:insert( myAnim2 )
myAnim1.x = 20
myAnim1.y = 80
local function pressFunction()
myAnim1.alpha = 0.7
end
local function releaseFunction()
myAnim1.alpha = 1.0
end
-- Make 2nd sprite draggable
myAnim1:setDrag{
drag=true,
onPress=pressFunction,
onRelease=releaseFunction,
bounds = { 0, 0, 320, 480 }
}
local rotate = function( event )
myAnim1.rotation = event.x
end
myAnim1:addEventListener( "touch",rotate)
In this code the image rotates while I drag it. I want rotation to happen after dropping the image at some place on the screen.
Can anyone solve this? Thanks in advance
use this formula to rotate the object in corona sdk
local rotate = function(event)
if event.phase == "ended" then
myAnim1.rotation = math.ceil(math.atan2( (event.y - myAnim1.y), (event.x -myAnim1.x) ) * 180 / math.pi) + 90
end
end
i think it useful to u.........
In your release function you could add this:
myAnim1:rotate(0)
...and that will set the object to being "normal" (right side up).
You also might want to look at the code where you're doing the rotating -- you're setting the degree of rotation to the x coordinate on the screen where you're touching. If that's what you're wanting to do, you're good. It just seems weird. :)
You have a listener set that responds to all "touch" events. The problem is that "touch" events are dispatched the entire time your finger is touching the object, while you are dragging it around. If you only want to respond when you let go then you need to react to event.phase:
http://developer.anscamobile.com/reference/index/eventphase-0
So your function would look like:
local rotate = function(event)
if event.phase == "ended" then
myAnim1.rotation = event.x
end
end
OR
Another approach is to listen for "tap" instead of "touch". Personally I'd go with the solution above for future flexibility in your code, but note that while "touch" events are dispatched the entire time the finger is touching, "tap" events are only dispatched when the finger is removed. So:
myAnim1:addEventListener("tap", rotate)

Resources