I want to change sprites when the user swipes left and right. Everything I found on stack overflow talks about changing textures. However I want to change the sprite completely.
here is my code that tries to do that
#implementation GameScene
SKSpriteNode* player;
SKSpriteNode* playerL;
SKSpriteNode* playerR;
- (void) addPlayer
{
//player right
playerR = [[SKSpriteNode alloc] initWithImageNamed: #"pummelManTestR.png"];
playerR.scale = 0.2f;
playerR.name = playerCategoryName;
playerR.position = CGPointMake(CGRectGetMidX(self.frame)*1.3f, playerR.frame.size.height *1.4f);
//player left
playerL = [[SKSpriteNode alloc] initWithImageNamed: #"pummelManTestL.png"];
playerL.scale = 0.2f;
playerL.name = playerCategoryName;
playerL.position = CGPointMake(CGRectGetMidX(self.frame)*1.3f, playerL.frame.size.height *1.4f);
//set the starting player position
player = playerL;
[self addChild:player];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint endPosition = [touch locationInNode:self];
if (touchStartPoint.x < endPosition.x && player != playerR) {
// Right swipe
//change the image for the player to the other side
player = playerR;
NSLog(#"right");
} else if (touchStartPoint.x > endPosition.x && player != playerL){
// Left swipe
//change the image for the player to the other side
player = playerL;
NSLog(#"left");
}
}
the NSLogs print the correct information but the sprite on the screen doesn't change.
Any help will be greatly appreciated.
player.texture = "your image"
player.name = "your name"
....
redefine the element, with this solution i'm test! all right!
Related
I'm making simple game for two players on Ipad, just like ping pong, when one finger is on screen player can react with his paddle, but when second finger is on his paddle, he cant move his paddle, but he move first paddle instead, I set up some NSLog and it says that both of them moves, but that isn't right,here are sample of my code:
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
//First player
CGPoint touchLocation = [touch locationInNode:self];
SKPhysicsBody* body = [self.physicsWorld bodyAtPoint:touchLocation];
if (body && [body.node.name isEqualToString: paddleCategoryName]) {
NSLog(#"Began touch on first paddle");
self.isFingerOnPaddle = YES;
}
//Second player
CGPoint secondTouchLocation = [touch locationInNode:self];
SKPhysicsBody* secondbody = [self.physicsWorld bodyAtPoint:secondTouchLocation];
if (secondbody && [secondbody.node.name isEqualToString: secondPaddleCategoryName]) {
NSLog(#"Began touch on second paddle");
self.isSecondFingerOnPaddle = YES;
}
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
//for first player
if (self.isFingerOnPaddle) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* paddle = (SKSpriteNode*)[self childNodeWithName: paddleCategoryName];
int paddleY = paddle.position.y + (touchLocation.y - previousLocation.y);
paddleY = MAX(paddleY, paddle.size.height/2);
paddleY = MIN(paddleY, self.size.height - paddle.size.height/2);
paddle.position = CGPointMake(paddle.position.x, paddleY);
NSLog(#"First paddle moving");
}
//for second player
if (self.isSecondFingerOnPaddle) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* secondPaddle = (SKSpriteNode*)[self childNodeWithName: secondPaddleCategoryName];
int secondPaddleY = secondPaddle.position.y + (touchLocation.y - previousLocation.y);
secondPaddleY = MAX(secondPaddleY, secondPaddle.size.height/2);
secondPaddleY = MIN(secondPaddleY, self.size.height - secondPaddle.size.height/2);
secondPaddle.position = CGPointMake(secondPaddle.position.x, secondPaddleY);
NSLog(#"Second paddle moving");
}
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
self.isFingerOnPaddle = NO;
self.isSecondFingerOnPaddle = NO;
}
What I do wrong, and what do I need to change to my code work, like it should
A better approach would be to sub class paddle and do your touch code inside the paddle subclass. This eliminates having to loop through touch arrays to see if your paddle is being touched, plus it makes the code a lot neater and easier to read. The only thing is you will have to style your node set up a little differently, because touch moved will not work outside of the paddle.
Here is how you style it
1) Subclass Paddle to SKNode
2) Create a child of SKSpriteNode for your actual paddle gfx
3) Paddle frame should be set at the width of your scene, with a height that is allowed for the player to touch (probably the height of the paddle gfx)
4) override the touch code inside Paddle, all touch code in the set should only relate to what is being touched by the paddle
5) Do all of your sliding logic inside these overrides
Now you have Paddle defined for the behavior you are looking for, you can add it to the scene where ever you like.
The nice part about this method is you eliminate a lot of duplicate code, (Since the paddles on both sides behave differently) and if you want to add some fun, you can add even more paddles to both sides to provide more variation. (Each player has a paddle in the middle of the play field, and the bottom, which can be moved independantly)
your logic is wrong, just add a touchArray property to store first touch and second touch, set it when touch begin then determine when touches moved.
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
if (node && [node.name isEqualToString: paddleCategoryName]) {
NSLog(#"Began touch on first paddle");
[self.touchArray replaceObjectAtIndex:0 withObject:touch];
}else if (node && [node.name isEqualToString: secondPaddleCategoryName]) {
NSLog(#"Began touch on second paddle");
[self.touchArray replaceObjectAtIndex:1 withObject:touch];
}
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
UITouch *firstTouch = self.touchArray[0];
UITouch *secondTouch = self.touchArray[1];
SKSpriteNode *paddle = [[SKSpriteNode alloc] init];
if (touch == firstTouch) {
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* paddle = (SKSpriteNode*)[self childNodeWithName: paddleCategoryName];
int paddleY = paddle.position.y + (touchLocation.y - previousLocation.y);
paddleY = MAX(paddleY, paddle.size.height/2);
paddleY = MIN(paddleY, self.size.height - paddle.size.height/2);
paddle.position = CGPointMake(paddle.position.x, paddleY);
NSLog(#"First paddle moving");
}else if (touch == secondTouch) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* secondPaddle = (SKSpriteNode*)[self childNodeWithName: secondPaddleCategoryName];
int secondPaddleY = secondPaddle.position.y + (touchLocation.y - previousLocation.y);
secondPaddleY = MAX(secondPaddleY, secondPaddle.size.height/2);
secondPaddleY = MIN(secondPaddleY, self.size.height - secondPaddle.size.height/2);
secondPaddle.position = CGPointMake(secondPaddle.position.x, secondPaddleY);
NSLog(#"Second paddle moving");
}
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
if (touch == self.touchArray[0]) {
[self.touchArray replaceObjectAtIndex:0 withObject:#""];
}else if (touch == self.touchArray[1]) {
[self.touchArray replaceObjectAtIndex:1 withObject:#""];
}
}
self.isFingerOnPaddle = NO;
self.isSecondFingerOnPaddle = NO;
}
I have a bubble, when touched will pop. This pop effects work fine with the code below. However when a spritekit particle effect is used which covers the bubble, the touch action does not work. When the particle effect finishes, the touch action will work. Does the particle effect have a non-touch field? and is there anyway around this? I attempted to change the z-position but that did not work.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
SKLabelNode *touchedNode = (SKLabelNode *)[self nodeAtPoint:positionInScene];
if ([[touchedNode name] isEqualToString:#"bubbleBall1"]) {
ballNode.position = bubble1.position;
[_gameNode addChild:ballNode];
[bubble1 removeFromParent];
[self playBubblePopSound];
}
}
This is the particle effect im using in the physics contact area.
SKEmitterNode *starCollectParticle = [SKEmitterNode emitterNamed:#"starCollect"];
starCollectParticle.position = star1Node.position;
starCollectParticle.zPosition = 0;
//starCollectParticle.particleLifetime = 2;
[self addChild:starCollectParticle];
It was the z.position. I change the z.position of the particles. This did not work. However also changing the z.position of the SKSpritenode did the trick.
-(void)addBubble1:(CGPoint)pos{
gameObjects = [SKTextureAtlas atlasNamed:#"GameObjects.atlas"];
bubble1 = [SKSpriteNode spriteNodeWithTexture:[gameObjects textureNamed:kBubble]];
bubble1.size = bubble1.texture.size;
bubble1.size = CGSizeMake(70,70);
bubble1.position = pos;
bubble1.name = bubble1CategoryName;
**bubble1.zPosition = 1;**
bubble1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bubble1.frame.size.width/2];
bubble1.physicsBody.affectedByGravity = NO;
bubble1.physicsBody.dynamic = NO;
bubble1.physicsBody.usesPreciseCollisionDetection = YES;
bubble1.physicsBody.categoryBitMask = bubble1Category;
bubble1.physicsBody.contactTestBitMask = ballCategory | completeObjectNodeCategory | completeObjectBoxNodeCategory;
bubble1.physicsBody.collisionBitMask = completeObjectNodeCategory | lineNodeCategory;
[_gameNode addChild:bubble1];
}
SKEmitterNode *starCollectParticle = [SKEmitterNode emitterNamed:#"starCollect"];
starCollectParticle.position = star1Node.position;
**starCollectParticle.zPosition = 0;**
//starCollectParticle.particleLifetime = 2;
[self addChild:starCollectParticle];
Working with Xcode, Spritebuilder and Cocos2d, I am trying to let the players sprite only jump 1 single time. So when the player taps the screen, the sprite jumps, and only when it has landed, the player should again be able to jump again. I haven't found any clear explanations so far, so hence my question.
Here's my code
Edit so that the code only lets the sprite jump once:
static const CGFloat scrollSpeed = 0.4;
BOOL isInAir;
#implementation GameScene
{
CCSprite *player1;
CCPhysicsNode *physicsNode1;
CCNode *ground1;
CCNode *ground2;
}
- (void)didLoadFromCCB
{
self.userInteractionEnabled = TRUE;
grounds = #[ground1, ground2]
}
- (void)update:(CCTime)delta
{
// moves the player to the right
player1.position = ccp(player1.position.x + delta * scrollSpeed, player1.position.y);
// move the camera with the player
physicsNode1.position = ccp(physicsNode1.position.x - (scrollSpeed *delta), physicsNode1.position.y);
// loop the ground
for (CCNode *ground in grounds)
{
// get the world position of the ground
CGPoint groundWorldPosition = [physicsNode1 convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.x <= (-1 * ground.contentSize.width))
{
// puts the ground piece that is about to leave the screen behind the last one
ground.position = ccp(ground.position.x + 600, ground.position.y);
//NSLog(#"player1 pos: %#", NSStringFromCGPoint(ground.position));
}
}
// clamp velocity
float yVelocity = clampf(player1.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
player1.physicsBody.velocity = ccp(0, yVelocity);
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if (isInAir == NO)
{
[player1.physicsBody applyImpulse:ccp(0, 150)];
isInAir = YES;
}
}
- (BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair player1:(CCNode *)player1 ground: (CCNode *)ground
{
NSLog(#"player is touching the ground");
isInAir = NO;
return YES;
}
#end
You can use a BOOL. Here is my code for a jump on touch:
BOOL isInAir;//Make this global.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
SKSpriteNode *node = [self nodesAtPoint:[touch locationInNode:self]];
if (isInAir == NO)
{
CGFloat impulseX = 0.0f;
CGFloat impulseY = 600.0f;
[node.physicsBody applyImpulse:CGVectorMake(impulseX, impulseY) atPoint:node.position];
isInAir = YES;
//other stuff
}
}
Then when you contact ground, set isInAir to NO.
I've used this same exact code before, and it worked perfect every time. However, I'm trying to use it to move the paddle sprite along a horizontal path at position 100 for y and it updates with the location of my touch but for some reason it's not moving at all. I can't spot what's wrong. Can someone take a look and let me know?
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
CGPoint newPosition = CGPointMake(location.x, 100);
//stop the paddle from going too far
if (newPosition.x < self.paddle.size.width/ 2) {
newPosition.x = self.paddle.size.width/ 2;
}
if (newPosition.x > self.size.width - (self.paddle.size.width/ 2)) {
newPosition.x = self.size.width - (self.paddle.size.width/ 2);
}
self.paddle.position = newPosition;
}
}
-(void) addPlayer:(CGSize)size {
SKSpriteNode *paddle = [SKSpriteNode spriteNodeWithImageNamed:#"paddle"];
//resize sprite
paddle.size = CGSizeMake(125, 31.25);
//position sprite
paddle.position = CGPointMake(size.width/2, 100);
//add physics body to paddle
paddle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:paddle.frame.size];
//change to static so wont be moved by physics
paddle.physicsBody.dynamic = NO;
//add sprite to scene
[self addChild:paddle];
}
-(instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]){
self.backgroundColor = [SKColor colorWithRed:(29.0f/255) green:(29.0f/255) blue:(29.0f/255) alpha:1.0];
//change gravity
self.physicsWorld.gravity = CGVectorMake(0, 0);
//add physics body to scene
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
[self addPlayer:size];
[self addBricks:size];
[self addBall:size];
}
return self;
}
Hi.
I'm having this weird problem in Sprite Kit. I'm using nodeAtPoint and categoryBitMask to detect whether the player is touching the ground when calling a jump method.
Everything's working as it should. But then — in order to reveal some optional buttons in a drawer — when I move the parent node with SKAction moveTo:CGPoint (I have both the ground and player as children of an SKNode), the player don't jump. I NSLog the pointBelowPlayer, and it is the same as before, but the blockNode.physicsBody is null! Might this be a bug in Sprite Kit, or am I missing something basic about inheritance and position?
The method for jumping:
-(void)playerJump {
// Player jump if on ground
CGPoint pointBelowPlayer = CGPointMake(_player.position.x, _player.position.y-_player.size.height);
SKNode *blockNode = [self nodeAtPoint:pointBelowPlayer];
if (blockNode.physicsBody.categoryBitMask == groundCategory){
[_player.physicsBody applyImpulse:CGVectorMake(0, 120.0f)];
}
}
The method for moving the parent node:
-(void)toggleDrawer {
if (bDrawerVisible) {
[_actionLayer runAction:[SKAction moveTo:CGPointMake(0, 0) duration:1]];
bDrawerVisible = false;
} else {
[_actionLayer runAction:[SKAction moveTo:CGPointMake(0, 200) duration:1]];
bDrawerVisible = true;
}
}
Creation of the SpriteNodes:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
_actionLayer = [SKNode new];
_player = [self createPlayer];
[_actionLayer addChild:_player];
_ground = [self createGround];
[_actionLayer addChild:_ground];
}
-(SKSpriteNode *)createPlayer {
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:#"redSquare.png"];
player.name = #"player";
player.scale = 0.5;
player.position = CGPointMake(screenWidth/3, player.size.height*2);
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.size];
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = groundCategory;
player.physicsBody.contactTestBitMask = noteCategory;
return player;
}
-(SKSpriteNode *)createGround {
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:#"ground.png"];
ground.name = #"ground";
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.dynamic = NO;
ground.physicsBody.categoryBitMask = groundCategory;
ground.physicsBody.collisionBitMask = playerCategory;
ground.xScale = screenWidth/ground.size.width;;
ground.position = CGPointMake(self.size.width/2, 0);
return ground;
}
Ok i solve the problem...
if you have more then one Node, on the position you tap, you will get null if the deepest node have no name, or the name of the deepest Node.
So use this to go through all nodes are found:
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
NSArray *buttonNodes = [self nodesAtPoint:location];
for (SKNode *node in buttonNodes)
{
NSLog(#"%#", node.name);
}
Additional you can use NSLog(#"%f", node.zPosition); to get the zPosition to see witch one is on top.
Node at point might be another node (maybe your background?).
Use node.name to name the nodes and you may check if node if the same by comparing names with equalToString: method.