SpriteKit Objective Jump - ios

I got a problem with my code. I added the swipe gesture to jump, but my objective won't jump at all. The NSLog for the Swipe gesture works, so it must be something wrong with my objective code.
-(void)Mensch{
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
Mensch = [SKSpriteNode spriteNodeWithTexture:MenschTexture1];
Mensch.size = CGSizeMake(45, 45);
Mensch.position = CGPointMake(self.frame.size.width / 5, Boden.position.y + 73);
Mensch.zPosition = 2;
Mensch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Mensch.size];
Mensch.physicsBody.dynamic = NO;
Mensch.physicsBody.allowsRotation = NO;
Mensch.physicsBody.usesPreciseCollisionDetection = YES;
Mensch.physicsBody.restitution = 0;
[Mensch runAction:Run];
[self addChild:Mensch];
}
- (void)didMoveToView:(SKView *)view
{
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
recognizer.direction = UISwipeGestureRecognizerDirectionUp;
[[self view] addGestureRecognizer:recognizer];
}
- (void)handleSwipe:(UISwipeGestureRecognizer *)sender
{
Mensch.physicsBody.velocity = CGVectorMake(0, 0);
[Mensch.physicsBody applyImpulse:CGVectorMake(0, 20)];
NSLog(#"Jump");
}

You need to set
Mensch.physicsBody.dynamic = YES;
According to Apple's documentation about the dynamic property of SKPhysicsBody:
The default value is YES. If the value is NO, then the physics body
ignores all forces and impulses applied to it. This property is
ignored on edge-based bodies; they are automatically static.

Related

Sprite Kit player can still leave screen

I've been trying to prevent my sprite that is controlled by a UIPanGestureRecognizer from leaving the screen. I've tried creating this with this code but the player is still able to leave the screen.
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = wallCategory;
player = [SKSpriteNode spriteNodeWithImageNamed:#"thePlayer.png"];
player.size = CGSizeMake(35, 35);
player.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
player.name = kPlayerName;
player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:player.size.width/2];
player.physicsBody.dynamic = NO;
player.physicsBody.usesPreciseCollisionDetection = YES;
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.restitution = 0;
player.physicsBody.collisionBitMask = enemyCategory | wallCategory;
player.physicsBody.contactTestBitMask = enemyCategory | bonusCategory;
[self addChild:player];
}
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
if (!pan) {
pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(dragPlayer:)];
pan.minimumNumberOfTouches = 1;
pan.delegate = self;
[self.view addGestureRecognizer:pan];
}
}
-(void)dragPlayer: (UIPanGestureRecognizer *)gesture {
CGPoint trans = [gesture translationInView:self.view];
SKAction *moveAction = [SKAction moveByX:trans.x y:-trans.y duration:0];
[player runAction:moveAction];
[gesture setTranslation:CGPointMake(0, 0) inView:self.view];
}
Any ideas?

SpriteKit change Image after swipe gesture

I am trying to code a game. I got an object, that can jump and slide.
I want to hold the animation of 'run' while jumping, but while sliding, I want to change the image. My problem: the image won't show off , if I just change the image to 'slide7'.
Nothing happens.
The slide animation should appear only for about 4 seconds, than go again into the run animation. Any suggestions?
My Code :
-(void)Mensch{
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
Mensch = [SKSpriteNode spriteNodeWithTexture:MenschTexture1];
Mensch.size = CGSizeMake(45, 45);
Mensch.position = CGPointMake(self.frame.size.width / 5, Boden.position.y + 73);
Mensch.zPosition = 2;
Mensch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Mensch.size];
Mensch.physicsBody.dynamic = YES;
Mensch.physicsBody.allowsRotation = NO;
Mensch.physicsBody.usesPreciseCollisionDetection = YES;
Mensch.physicsBody.restitution = 0;
Mensch.physicsBody.velocity = CGVectorMake(0, 0);
[Mensch runAction:Run];
[self addChild:Mensch];
}
- (void)didMoveToView:(SKView *)view
{
UISwipeGestureRecognizer *recognizerDown = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(handleSwipeDown:)];
recognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
[[self view] addGestureRecognizer:recognizerDown];
}
-(void)handleSwipeDown:(UISwipeGestureRecognizer *)sender
{
[Mensch removeAllActions];
Mensch = [SKSpriteNode spriteNodeWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
}
You are replacing the Mensch object. More specifically, you are replacing the pointer you have to the object, but that doesn't stop it from being displayed in the scene.
You can either:
Replace the SKTexture inside the sprite, which should be displayed immediately. This means Mensch.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
Replace the Sprite. This entails removing the current sprite ([sprite removeFromParent]) and adding the new one ([self addChild:newSprite]).
I think you want the first one, since you don't have to recreate the Mensch object.
Might I add that you are performing a lot of Mensch-specific logic in this object? It would be better (from an Object Oriented standpoint) to move this creation code to an SKSpriteNode subclass Mensch.
#interface Mensch : SKSpriteNode
- (void) displayRunAnimation;
- (void) displaySlideAnimation;
#end
#implementation : Mensch
- (instancetype) init {
self = [super init];
if (self) {
// your initialization code here, including physics body setup
[self displayRunAnimation];
}
return self;
}
- (void) displayRunAnimation {
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
// if you plan to call this often, you want to cache this SKAction, since creating it over and over is a waste of resources
[self runAction:Run];
}
- (void) displaySlideAnimation {
[self removeAllActions];
self.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
// re-start the runAnimation after a time interval
[self performSelector:#selector(displayRunAnimation) withObject:nil afterDelay:4.0];
}

Moving a camera in SpriteKit

//UPDATE
The updated code has been added that works as I expected. See didSimulatePhysics method in the updated code below. In my case, I only care about moving a character left or right on the x axis where 0 on the x axis is the absolute left and right on the x axis is a configurable value. The Apple adventure game really helped a lot too.
//ORIGINAL POST BELOW
I'm working with Apple SpriteKit and I'm struggling to implement a camera as I would like it to behave. What I've done in the code is load a sprite character, two buttons, and a red box that is off to the right outside of the view at the start. What I'd like to be able to do is move the character with the buttons, and once the player reaches the middle or end of the screen, the camera will then re-adjust to uncover what couldn't be seen in the view. So moving to the right should eventually show the red box that is off outside of the view initially once the player gets there. However, with the code I'm using below, I'm unable to get the camera to follow and adjust the coordinates to the main character at all. I've looked at Apple's advanced scene processing doc as well as a few other stack overflow posts but can't seem to get it right. If anyone could offer some advice it would be appreciated.
#define cameraEdge 150
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//320 568
self.backgroundColor = [SKColor whiteColor];
myWorld = [[SKNode alloc] init];
[self addChild:myWorld];
mainCharacter = [SKSpriteNode spriteNodeWithImageNamed:#"0"];
mainCharacter.physicsBody.dynamic = YES;
mainCharacter.name = #"player";
mainCharacter.position = CGPointMake(20, 20);
CGRect totalScreenSize = CGRectMake(0, 0, 800, 320);
SKSpriteNode *box = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(60, 60)];
SKSpriteNode *boxTwo = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(60, 60)];
SKSpriteNode *boxThree = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(60, 60)];
boxThree.position = CGPointMake(40, 50);
[myWorld addChild:boxThree];
boxTwo.position = CGPointMake(1100, 50);
box.position = CGPointMake(650, 50);
[myWorld addChild:box];
[myWorld addChild:boxTwo];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:totalScreenSize];
self.physicsWorld.gravity = CGVectorMake(0, -5);
mainCharacter.name = #"mainCharacter";
mainCharacter.physicsBody.linearDamping = 0;
mainCharacter.physicsBody.friction = 0;
mainCharacter.physicsBody.restitution = 0;
mainCharacter.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mainCharacter.size];
[myWorld addChild:mainCharacter];
[self addChild:[self buildLeftButton]];
[self addChild:[self buildRightButton]];
}
return self;
}
- (void)didSimulatePhysics
{
SKSpriteNode *hero = mainCharacter;
if(hero)
{
CGPoint heroPosition = hero.position;
CGPoint worldPosition = myWorld.position;
NSLog(#"%f", heroPosition.x);
CGFloat xCoordinate = worldPosition.x + heroPosition.x;
if(xCoordinate < cameraEdge && heroPosition.x > 0)
{
worldPosition.x = worldPosition.x - xCoordinate + cameraEdge;
self.worldMovedForUpdate = YES;
}
else if(xCoordinate > (self.frame.size.width - cameraEdge) && heroPosition.x < 2000)
{
worldPosition.x = worldPosition.x + (self.frame.size.width - xCoordinate) - cameraEdge;
self.worldMovedForUpdate = YES;
}
myWorld.position = worldPosition;
}
}
-(SKSpriteNode *)buildLeftButton
{
SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:#"left"];
leftButton.position = CGPointMake(20, 20);
leftButton.name = #"leftButton";
leftButton.zPosition = 1.0;
return leftButton;
}
-(SKSpriteNode *)buildRightButton
{
SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:#"right"];
leftButton.position = CGPointMake(60, 20);
leftButton.name = #"rightButton";
leftButton.zPosition = 1.0;
return leftButton;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"leftButton"])
{
[mainCharacter.physicsBody applyImpulse:CGVectorMake(-120, 0)];
}
else if([node.name isEqualToString:#"rightButton"])
{
[mainCharacter.physicsBody applyImpulse:CGVectorMake(120, 10)];
}
}
If you want the view to always be centered on your player's position, modify your code with these points in mind:
1) Create a SKNode and call it myWorld, worldNode or any other name like that.
2) Add the worldNode [self addChild:worldNode];
3) Add all other nodes to the worldNode, including your player.
4) In the didSimulatePhysics method, add this code:
worldNode.position = CGPointMake(-(player.position.x-(self.size.width/2)), -(player.position.y-(self.size.height/2)));
Your view will now always be centered on your player's position.
Update May 2015:
If you are using a map created with Tiled Map Editor, you can use the free SKAToolKit framework. Features include player camera auto follow, test player, test HUD and sprite buttons.

Change image when touched

I need an image to change itself when it's touched.
At the moment the image that changes is the next image that spawns and not self.
#implementation MyScene2
{
Marsugo *marsugo;
SKAction *actionMoveDown;
SKAction *actionMoveEnded;
SKTexture *rescued;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
// Initializes Background
self.currentBackground = [Background generateNewBackground];
[self addChild:self.currentBackground];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
for (SKNode *node in nodes) {
if ([node.name isEqualToString:playerObject]) {
rescued = [SKTexture textureWithImageNamed:#"rescued"];
marsugo.texture = rescued;
// this is changing the image of the next marsugo that spawns instead of self.
}
}
}
}
-(void)addMarsugos
{
// the marsugo is being initialized inside this method, that might be the issue i believe
// Create sprite
marsugo = [[Marsugo alloc]init];
marsugo.xScale = 0.3;
marsugo.yScale = 0.3;
marsugo.zPosition = 75;
// Bounds + Spawn Positions
int minX = marsugo.size.width;
int maxX = self.frame.size.width - marsugo.size.width;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
marsugo.position = CGPointMake(actualX, self.frame.size.height + 50);
[self addChild:marsugo];
// Spawn Timer
int minDuration = 1;
int maxDuration = 10;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Movement Actions
actionMoveDown = [SKAction moveTo:CGPointMake(actualX, CGRectGetMinY(self.frame)) duration:actualDuration];
actionMoveEnded = [SKAction removeFromParent];
[marsugo runAction:[SKAction sequence:#[actionMoveDown, actionMoveEnded]]];
NSLog(#"Marsugo X: %f - Speed: %i", marsugo.position.x, actualDuration);
}
#end
Like i said previously, i need the self sprite to change texture and not the "next spawning sprite".
Any help fixing this would be appreciated, thank you.
You don't want to use touchesBegan - you're better off using a tap gesture recognizer. Below I have _testView, which is an instance variable I create and add to the view in viewDidLoad. I then created a tap gesture recognizer that calls a function when the view is tapped, and that function changes the color of the view - but in your case you can call your function that changes the image:
- (void)viewDidLoad {
[super viewDidLoad];
// create the test view and add it as a subview
_testView = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 100, 100)];
_testView.backgroundColor = [UIColor redColor];
[self.view addSubview:_testView];
// create the tap gesture recognizer and add it to the test view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(changeColor)];
[_testView addGestureRecognizer:tapGestureRecognizer];
}
- (void)changeColor {
// here I'm changing the color, but you can do whatever you need once the tap is recognized
_testView.backgroundColor = [UIColor blueColor];
}
This results in this at first:
Then when I tap the view:
You can override -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event in your Marsugo class and change the texture there. That way, you won't have to check if the touch is inside your node because that method won't be called if it's not.

How to calibrate a sprites touchLocation after zoom

I add a sprite on the background sprite and then zoom (pinch) the background and by that the sprite as well.
After the zoom i want to be able to click right on the "zoomed" sprite and then move (pan) it. However, to catch the sprite i have to click outside of the sprite. I guess that is due to the zoom and coordinates.
I would really need some help how to make it so that i always only need to click on the actual sprite if it is in it's original state or zoomed up or down.
I will also implement UIScrollView so i can move around the background but would like to fix this first.
Here is the code i use to test this:
- (void)didMoveToView:(SKView *)view {
_background = [SKSpriteNode spriteNodeWithImageNamed:#"background"];
_background.name = #"background";
[_background setAnchorPoint:CGPointZero];
// _background.anchorPoint = CGPointMake(0.0, 0.1);
self.scaleMode = SKSceneScaleModeResizeFill;
[self addChild:_background];
SKLabelNode *texT = [SKLabelNode labelNodeWithFontNamed:#"Ariel"];
texT.text = #"X";
texT.position = CGPointMake(160, 260);
[_background addChild:texT];
_panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:_panGesture];
_pinchGesture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(handlePinch:)];
[self.view addGestureRecognizer:_pinchGesture];
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.2;
sprite.yScale = 0.2;
sprite.position = CGPointMake(160, 260);
sprite.name = #"sprite";
[_background addChild:sprite];
}
- (void)handlePan:(UIPanGestureRecognizer *)sender {
NSLog(#"handlePan:");
if (![_currentNode.name isEqualToString:#"background"]) {
_touchLocation = [sender locationInView:sender.view];
_touchLocation = [self convertPointFromView:_touchLocation];
_currentNode.position = _touchLocation;
}
}
- (void)handlePinch:(UIPinchGestureRecognizer *) sender {
NSLog(#"handlePinch: %f",sender.scale);
[self runAction:[SKAction scaleBy:sender.scale duration:0]];
sender.scale = 1;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"touchesBegan");
//////DO THE FIRST TOUCH//////
_actualTouch = [touches anyObject];
_touchLocation = [_actualTouch locationInNode:self];
_currentNode = [self nodeAtPoint:_touchLocation];
NSLog(#"TouchBegan:_touchLocation:%#", NSStringFromCGPoint(_touchLocation));
NSLog(#"_currentNode.name: %#", _currentNode.name);
}
#end
I had this same problem and I found that the solution is to resize instead of scale. The physics seem to still work as expected and now the touch events will too. I came across this solution here. The scaleMode in your didMoveToView needs to be changed as well, see below.
self.scaleMode = SKSceneScaleModeAspectFill;
- (void)handlePinch:(UIPinchGestureRecognizer *) sender {
NSLog(#"handlePinch: %f",sender.scale);
//[self runAction:[SKAction scaleBy:sender.scale duration:0]];
self.size = CGSizeMake(self.size.width/sender.scale,self.size.height/sender.scale);
sender.scale = 1;
}
In my own implementation I had set upper and lower limits to the zoom level as well.

Resources