Detecting touch pressure (force) with swift (SpriteKit) - ios

So, I've got this game with Swift on Xcode where I have a SKShapeNode that jumps. I would like, though, to have this jump commensurated to the touch the player does. Hence, a small tap will get a small jump and a more brisk one gets a higher jump. How is it possible to do this (mind that I do not want any 3D touch options)?

Usually, this sort of thing in games is done by timing the press and altering the jump height accordingly. (Which is also why using physics in 2D platforming games is tricky for this kind of thing).
The way it is done in games like Super Mario is to have the start of the jump movement begin as soon as the touch is pressed. Then after some amount of time (as the character is half way "up" the jump) you check to see if the touch is still down. If it is you can make the jump animation continue upwards. If the touch is released then start to bring the jump down again.
That way a short touch is a small jump but a longer touch is a higher jump.
If you have a smallJumpAction and bigJumpAction at the moment then what you could do is break it down into smaller parts...
beginJumpAction which is triggered immediately and then... smallJumpEndAction and bigJumpEndAction.
This could be done with a sequence action...
let beginJumpAction = //move the player upwards slightly
let smallJumpEndAction = //begin to move the player down
let bigJumpEndAction = //move the player up a bit more and then move down
let blockAction = SKAction.run {
if touchStillDown {
player.runAction(bigJumpEndAction)
} else {
player.runActgion(smallJumpEndAction)
}
}
player.runAction(SKAction.sequence([beginJumpAction, blockAction])
Something like this anyway. There are many ways to approach this though :)

Related

Draw circle and drag touch around SKSpriteNode

I am making a Sprite Kit game where the player (basically a stickman) has a running animation and a parallax scrolling background.
Now I have enemies that come near my player. To destroy these enemies sometimes I have to touch the enemies node to launch a rocket or attack them with an attack button or just jump over them.
Everything is working fine, but I want to add some extra moves to destroy them. I want some enemies that you can just destroy if you have drawn a whole circle around them. So imagine they come and you make a circle and then my player launch a laser or something. The problem is I have no idea where to start.
I haven't found anything on the internet. If it's too complicated or almost impossible how about touching my player node and dragging to the enemy?
EDIT: I think I have to create a custom GestureRecognizer that recognizes if a circle is drawn around a sprite and then runs the code. I don't know how this works ?
Yes, it's too complex. Not just from a coding point of view, but also from that of the player's experience.
Anything that requires complex gestures over a large amount of glass is annoying for the player because they're never going to have the same experience. Their finger's moisture and oil content always changes, as does the ambient temperature and cleanliness of their screen.
So big gestures required to be performed quickly (a gaming input like this) will sometimes be fun and smooth, and other times degrade as an experience based on the nature of the above properties.
Best to avoid them for a game's best possible experience.
If you must do it, there's two ways to research how.
Seek out "custom gesture" creation and utilisation through documentation and google, etc.
Think about using some kind of array to store all the points where the player's finger moves through during that circle gesture and attempt to discern if an enemy is within that space and then act accordingly.
--- probably other ways, too. But these jump to mind.

How to make player-sprite "jump" using SpriteKits built-in physics engine

I'm a little confused trying to find a reliable way to make my player-sprite jump when the user taps the screen.
So far I've been doing this:
if(self.physicsBody.velocity.dy == 0.0 && _shouldJump) {
[self.physicsBody applyImpulse: CGVectorMake(0.0, 195.0);
}
Which works, but I'm looking for something more reliable and better. Specifically, right now, the user cannot jump when he's sliding down a slope, or on the edge of a block about to fall down (because .dy isn't exactly 0). I'd like the player to be able to jump in these cases too.
I've tried something like .dy < 5.0 && .dy > -5.0, but then he's able to jump when he's at the top of his current jump, making him jump even higher.
Is there a way of checking which physicsBodies my player-sprite is in contact with maybe? Or do you have any other suggestions?
I have read Ray Wenderlich's tutorial on how to create a Platform Game Like Super Mario, but they're building their own custom Physics Engine. I'd like to stick with the one built into SpriteKit, mainly becuase of the collision handling and detection.
EDIT 1:
Bear in mind that I cannot set a flag _isOnGround = YES every time the Player touches the ground in didBeginContact, as he is bouncy and will bounce up-and-down a few times before coming to a stop. I'd prefer the user to be able to jump as soon as he touches the ground when he's holding the screen, but this is not necessary.

How to make a jump sprite

I'm building a Mega Man clone for a school project and I just finished the code to make my character jump and it works perfectly. But I need it to display the actual jump sprite of Mega Man. This is the whole code in Game1:
http://pastebin.com/py7Y7mtD
The AnimatedSprites jt helps you decide the direcrtion and display the sprite. Doesn't matter right now if it's efficient. It's just for starts. When I'll get to more complex stuff I'll work on it.
MegamanJump has the jump sprite(5 or 6 sprites displayed from left to right)
MegamanJump_Flip is the same thing but to the left(again, forget about efficiency).
Basically you want to switch out the current drawable sprite with the sprite you want to draw depending on moving direction or moving direction + jump.
Since you already have a running update method, all you have to do is write another method that does this for you, a state variable to track what is happening and the actual states it can have, preferrably in an enum for clarity.
Essentially
private void swapSprite() {
if(state == CharacterState.LEFT) {
sprite = leftDrawable;
}
//etc other single ifs
if(state == CharacterState.LEFTJUMPING) {
sprite = leftJumpDrawable;
}
}
Change the states on button press combinations, call this method after checking for movement.
I hope this helped!

Sometimes sprite is pushed away when touches another sprite

I am making a game using cocos2d and SpriteBuilder. There is a hero sprite and coins, that should be collected by him. That's a horizontal scrolling game, so every time hero touches a coin, this coin changes it's x-coordinate 2000px to the right and new y-coordinate is generated at random. Using update method I move it to the visible area as a "new" coin. But when hero flyies by and doesn't collect it ,coin must change coordinates only when it's already off the screen, so I tried this solution:
-(void)update:(CCTime)delta{
_coin.position=ccp(_coin.position.x - delta * scrollSpeed, _coinY);
if (CGRectIntersectsRect(_hero.boundingBox,_coin.boundingBox)) {
_coinY=arc4random() % 801 + 100;
_coin.position=ccp(_coin.position.x + 2000.f,_coinY);
}
else if(_hero.position.x >= _coin.position.x + 150){
_coinY=arc4random() % 801 + 100;
_coin.position=ccp(_coin.position.x + 2000.f,_coinY);
}
It works,but after that I found a small bug(I am not sure, whether it's related to this code) : sometimes, when hero touches coin, hero is like pushed away to the left. I have no idea why.
How to fix it?
Is that way, that I am using for coin ,right?
I see 2 issues with your approach:
1. Hero bounces of the coin.
To fix this you need to make your coin a sensor. This way you will still be notified about collisions, but the hero will simply pass through the coin without actually hitting it.
I'm not sure if you can set this in SpriteBuilder, but probably you can enumerate all coins after loading the scene and set sensor property to YES:
coin.physicsBody.sensor = YES;
Things like this is one of the reasons I believe you first need to learn pure Cocos2D and only then use tools making your life easier such as SpriteBuilder. Which is a great tool, but in some cases you still need know what happens behind the scenes.
2. You're mixing physics with things like CGRectIntersectsRect
If you're using physics engine you need to detect collisions via collision delegate and not by checking CGRectIntersectsRect in update:.
It is hard to explain how to do this in a few sentences, so here is a nice tutorial that shows how to detect collisions in Cocos2D v3 and of course there is a chapter about this in my book.
By the way you shouldn't use update:at all when manipulating physics nodes, use fixedUpdate: instead.
I hope this will help you to solve your issue.

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

Resources