Corona SDK, flappy bird bounce effect - lua

I'm developing a game where the character bounces much like the "Flappy Bird" character.
I have it working well, but I'm handing the bouncing effect with a touch event and when the user double touches really quick it basically doubles the force behind the player.
Also, take for example if the player is dropping from a higher height then the gravity seems to be too much and I have to touch many more times to bring the player back up, and I just can't seem to figure out how to make every touch consistent with the amount the character bounces.
Here is my function for the bounce effect:
function flyUp(event)
if event.phase == "began" then
if gameStarted == false then
player.bodyType = "dynamic"
instructions.alpha = 0
tb.alpha = 1
addColumnTimer = timer.performWithDelay(1000, addColumns, -1)
moveColumnTimer = timer.performWithDelay(2, moveColumns, -1)
gameStarted = true
player:applyForce(0, -300, player.x, player.y)
else
player:applyForce(0, -460, player.x, player.y)
end
end
end
...and here is where my player is defined as a physics body:
physics.addBody(player, "static",
{ density=.106, bounce=.1, friction=1, radius = 30 })
any help would be extremely appreciated, i just want the player to always move up the same amount no matter how many taps and how much he has dropped.
Thanks

You're probably forgetting that force determines acceleration, not velocity. So if you have an object moving downwards at speed V (V is positive value, since downward is positive in Corona) then applyForce() applies a momentary upward force (duration: one time frame) to the object, so the object's downward velocity will decrease a little bit (decelerate as a result of the upward force), but gravity is constant so you'll have to press multiple times to get enough deceleration.
What you probably want is to directly setLinearVelocity() to a negative value, thus making it look as though the object "bounced" upwards upon touch; gravity will then give it a parabolic trajectory. Also, if the object is always given the same linear velocity, it doesn't matter how many taps occur, although each tap will counter the effect of gravity with same upwards velocity.

Two ideas:
To avoid the double-touch/double-bounce, apply a cooling off period. When the user touches, ignore additional touches for some given amount of time.
Strictly speaking, you are getting the same amount of bounce (or upward force) per touch. It sounds like you want a variable bounce depending on the current velocity toward the ground. This shouldn't be too hard. Try applying more upward force in player:applyForce depending on the velocity on the y axis. If the player isn't falling, a smaller amount of force is needed. If they are falling at terminal velocity, a much larger force is needed.
It sounds like you recognize that "perfect" physics are less fun in your game. That's often the case. You'll likely need to experiment until something feels right.

Related

SpriteKit Jumping and moving issues

I'm fairly new to swift, and have been working on a game for fun, and i'm running into something I can't quite get my head around.
When the run button is pressed, the character moves forward with the following function
func move(dt: CGFloat) {
position.x += moveRate * dt
}
And when the jump button is pressed, the character jumps with the following function
func jump() {
physicsBody?.applyImpulse(CGVector(dx: 0, dy: jumpRate))
run(jumpAnimation!)
}
both work fine, but consider this senario. The player is running, and then they jump while still moving. While in the air, the player releases the move button and the player's x position stops dead. This obviously feels very unnatural, and i would like the player's x position to ease out.
Now i have also played with moving the character with physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) which would give that effect, but he seems to just gain more and more speed and you don't get a constant rate or "max speed" so to speak.
Could anybody share some insight with me? I'd love to learn anything I can about spritekit and game development in general. Thanks!
You should try to set the velocity instead of setting the X position. When setting the position you bypass all the physics behaviors.
You should also try to set it only when you actually press a button.
func move(dt: CGFloat) {
if Math.abs(moveRate) > 0.1 { // If player initiates movement. You can increase the value 0.1 if you want to filter move values
velocity = CGVector(dx: moveRate, dy: veloxity.dy)
}
}
It your character moves indefinitely like in space, linearDamping will be useful. it's used to simulate air friction, so values closer to 1 means more friction and values closer to 0 means less friction.
linearDamping = 0.85
Also, this way, moveRate isn't dt dependent but it should be lowered.
Try it, I haven't tested it yet, but that's basically how I would do it.
There are two schools of thought on platformer game "physics".
Don't use physics, do everything with positional incrementation.
Do everything with physics, since positional changes mess up physics
Since you're using physics for jumping, and physics jumping is fun:
There are three ways to create movement in a physics system:
Set the velocity as and when required. This is what Crazyrems is suggesting
Apply impulses as needed to increase and decrease rates of movement
Apply forces over time that increase or decrease rates of movement
Use fields to induce motion (too complex for this, and messy, but fun)
What you're attempting, with your physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) is the application of force over time. Number 3 in the list above. The longer you continue applying this force the faster the character moves.
Each of these techniques can be made to work, but they all need compensation for their various limitations and methodologies of simulation.
In the case of your approach, you need monitor speed and to set a maximum speed. Having reached maximum speed, if the player is still holding the button, only provide enough force to maintain speed (assuming you're using some form of constant resistance to slow the character).
Monitoring speed combined with force-over-time creates interesting acceleration trait possibilities - like very strong initial acceleration and then taper off acceleration as the player nears their maximum speed.
Slowing down when the player releases the button is the next focus. In all these approaches you need something that slows the player. This can be the application of opposing forces, or the use of friction or damping, as provided by the physics engine.

SceneKit – How does the option impulse works for the method applyForce?

When applying a force on a dynamic body, it seems that this force won't be persistent even if impulse is set at false.
I have a sphereNode with a physicsBody : dynamic, mass=1 and damping=0. World gravity is set at (0,0,0).
If I apply a force in a gesture recognizer :
func Detected(sender: UILongPressGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began {
sphereNode.physicsBody!.applyForce(SCNVector3Make(1, 0, 0), impulse: false)
}
if sender.state == UIGestureRecognizerState.Ended {
sphereNode.physicsBody!.clearAllForces()
}
}
Then the velocity goes to 0.0166 as if the force was applied only during one frame and stay constant unless I trigger the gesture again.
The only way to make it work as a true constant force is to call applyforce in the renderer loop but then the only difference with the non-impulse applyforce is a factor of 60. I don't think that is the expected behavior.
I think that is how it's supposed to work.
You use an impulse when you want to instantaneously change a body's momentum. For example, you might use impulse to fire a ball from a cannon. Like a cannon, it's "fire and forget"; you call the method and the ball flies off until it hits something.
You use a force (impulse:NO) when you want to make a continuous effect. That is, it doesn't cause a continuous effect (otherwise there'd have to be API for enumerating and canceling individual continuous forces on each body), but it gives you a tool to use when you want to do something on every frame to create a continuous effect.
For example, you'd use a force if you want your spaceship to accelerate when you press a button, and keep accelerating until you release it. While the button is held down, just call applyForce every time your update method runs.
The impulse toggle does two things for the two cases:
As you've noticed, it changes the units of magnitude to be appropriate for each case.
When using impulse:NO, it delays evaluation until the end of the current frame cycle. So you can apply several different forces during one update, and when SceneKit simulated physics for the next frame, it'll apply the sum of the forces (unless you call clearAllForces first).

Simulating the "Change Directions on Ice" Effect

I really hope someone understands what the effect is that I am asking for. Have you ever played the games where a character is sliding one way and when you try and change the characters direction it is not immediate as they need to slow down in the initial direction before they can start sliding the other way? The new game on the App Store 'Swing Copters' by the maker of Flappy Bird is exactly the effect I am talking about. Can someone please help me create this effect in SpriteKit. I have already tried achieving it by applying different forces but I am either not doing it correctly or the effect isn't possible with forces.
Thanks in advance!
Maybe you want to try working with some acceleration-stuff.
Let's think about the gravity on the earth (a = 9.81 metres/s²). If you throw a ball straight to the sky, the Ball is starting with a velocity of: lets say 5metres per second(positive velocity).
After a short time, the gravity is pulling the velocity of the ball down to 0, so the ball can't get any higher. Right after this, the gravity pulls the ball down until it hits the ground. (Negative velocity)
If we're talking about this in a game, where the ball doesn't move up or down but from left to right, you can use something like this. The moment when you throw the ball, is the moment in the game where you send the command to change the direction. The ball keeps going in the direction where it used to go, gets slower, stops, changes the direction and finally gets faster and faster until you send another command to change the direction again(Or it hits the Wall/the ground). In that case you have to inverse the acceleration you want to use, so the whole thing repeats in the other way.
If the ball should move to the right, positive acceleration,
if the ball should move to the left, negative acceleration.
As formula you can use something like
v = (int) (a * t + v0);
v0 = v;
v is the next velocity, a is the acceleration you want to use, t is the spent time and v0 is the current velocity. t should count the nano-time since the last direction-change. (int) casts the whole thing to an integer, so you can use this directly to move the ball/graphics on the screen.
Repeat this each frame.
For the direction change you can use
t = 0;
a = a * (-1);
t has to be 0 again, otherwise it gets buggy.
I hope this was helpful.

Sprite Kit Game: Telling Free Fall Death From Successful Jump

I have my first game application in development. In this game, the only character that the user controls will get to jump from one block to another. It's like Mario (in Mario Brothers) jumping from one moving lift to another. If he fails, he'll die. So how could you tell a free fall from a short fall as a result of a successful jump? One thing I thought I could do is measuring character's vertical velocity. So I have the following lines of code. It's used with didSimulatePhysics
SKNode *player = [self childNodeWithName:#"//player"]; // It's the node characterizing the game character
CGVector v = player.physicsBody.velocity;
if (v.dy < -2000) {
[self endTheScene:kEndReasonLose]; // The character has died from free fall => game is over
}
When the game character jumps, the game application can record a vertical velocity of -2022.466797. So this measure won't work. What else can I do? Set an invisible bar and see if the game character has touched it with intersectsNode? That can also fail. I have never developed a game before. So I don't know how they do it, which kind of makes me realize how impressive Nintendo game developers are. Some 30 years later, I still can't do it.
Thank you for your advice.
Update
The following can tell whether or not the character has died from free fall, I think.
- (void)didSimulatePhysics {
if (self.isMoving) {
// isMoving is an instance variable (BOOL): YES if the game has started
CGVector v = player.physicsBody.velocity;
BOOL hasFallen = self.lastFallenDate ? [self.lastFallenDate timeIntervalSinceNow] < -2.0 : YES; // lastFallenDate is an instance variable recording the time when the character fell last time
if (hasFallen) {
if (v.dy < -1500) {
[self endTheScene:kEndReasonLose];
}
}
}
}
Yet, I think Apple has a bug to fix as far as SKAction is concerned. No matter I do, audio will kick in exactly about 5 seconds after the game started although the character is not falling.
- (void)endTheScene:(EndReason)endReason {
if (endReason == kEndReasonLose) {
SKAction *lossAudio = [SKAction playSoundFileNamed:#"failureAudio.caf" waitForCompletion:NO];
[self runAction:lossAudio];
}
}
Super Mario Bros was a tile-based game: the screen was divided up into a number of square regions each of which being represented by a value. Each tile would have a separate sprite blitted to that region of the screen depending on the tile id, and based on that tile's properties Shigeru et al could determine if mario could stand on it, if it would hurt him or if he was free to fly. There's no magic to this, you just have to check where he is!
If you aren't concerned with the damage your Mario will take (i.e can he land a jump from any height as long as it is onto solid ground) this is easy: If his y position is ever lower than the lowest solid platform in your world, he has died:
Ignore velocity; check y position.
If you don't want him to survive a jump from the top of a building, you need to look at his velocity: Is he travelling fast enough that the force of impact would end his life?:
Ignore y position; check velocity
How do you know he is on a platform? A physic node?
Honestly, you can put a 'floor' plane stretching your world and anchor it at the bottom of your scene to catch dying plumbers and it won't impact performance - infact it will be more efficient than running these checks in your Update method.
Edit to add
Don't bother with intersections, -(void)didBeginContact:(SKPhysicsContact*)contact will alert you to any collision that occurred as long as you are a delegate. Skip the update loop completely: this will trigger for any collisions and won't be missed

applyForce Speed going faster

Hey I have this problem where If I tap and hold the screen, the player gains speed going up on Y.
What I would prefer is that the player taps the screen (and holds to float up) and he goes up at steady speed (without getting quicker).
Here is the function for float speed and touch event:
function activateJets(ship,event)
ship:applyForce(0, -1.0, ship.x, ship.y)
print("run")
end
function touchScreen(event)
print("touch")
if event.phase == "began" then
ship.enterFrame = activateJets
Runtime:addEventListener("enterFrame", ship)
end
if event.phase == "ended" then
Runtime:removeEventListener("enterFrame", ship)
end
end
Runtime:addEventListener("touch", touchScreen)
Sorry if this doesn't make sense. Here is a general idea of what I want:
player touches screen (and holds)
object then floats up at consistent speed (no speed gain)
player releases touch
object drops normally
The physics involved prevents you from doing that: you are applying a constant force to your ship. According to Newton's laws (simulated by the physics library), this means a constant acceleration, thus a linear increase in speed.
The behavior you want (which is not coherent with real physics) is an instant acceleration to your target speed and then no speed variation. Thus it should be enough to set the speed of your ship to a constant value using shipSetLinearVelocity() inside your activateJets function. Of course you should reset the speed to zero when the touch has ended.

Resources