How to create button in sprite kit iOS (similar to toggle button) - ios

I am trying to make a button in sprite kit using SKSpriteNode. I want the button image to change when it is pressed and revert back to old image as soon as the press ends. What i have done till now is following:-
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.startTouch = [[touches allObjects][0] locationInNode:self ];
for (UITouch *touch in touches){
CGPoint position = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:position];
if ([node.name isEqualToString:#"missileButton"]) {
TEMissileButtonNode *button = (TEMissileButtonNode*) node;
button.isPressed = YES;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches){
CGPoint position = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:position];
if ([node.name isEqualToString:#"missileButton"]) {
TEMissileButtonNode *button = (TEMissileButtonNode*) node;
button.isPressed = NO;
}
}
}
inside the update method i am calling this method to check if the touch has ended
-(void)changeMissileButton{
if (self.missileButton.isPressed) {
[self.missileButton addMoreMissileButtons];
[self.missileButton setTexture:[SKTexture textureWithImageNamed:#"missileButtonPressed"]];
}else{
[self.missileButton setTexture:[SKTexture textureWithImageNamed:#"missileButtonDeselected"]];
[self.missileButton hideMissileButtons];
}
}
The issue is that the touch doesn't get registered at times. Sometimes it works the way i want. When i touch it, its texture changes and when i remove my finger, the texture reverts back to old texture. But most of the times, the button doesn't react to my touch. Am i missing something?

Use touchesBegan, touchesMoved, touchesEnded.
touchesBegan - check if the button (SKSpriteNode) contains the touch. If so, change you button texture.
touchesMoved- if button does not contain touch, change texture back.
touchesEnded- if touch stayed within button during touchesMoved, change texture back.
Above is the logic for how to efficiently accomplish what you are trying to do. Yes, it is annoying Spritekit decided to abandon buttons.

Related

How to make an animated button in Sprite Kit

I have been trying to implement a method where the user can press down the play button, it then changes the texture to the pressed down image. Then, if the user decides not to continue with their action. Such as starting the game. They can then simply drag out of the sprite's frame/body which will then no longer detect the touch as one which will start the action.
The problem with this implementation is that I can't make the touch on the button cancel if the user drags outside of the play button's sprite frame.
The code you will see below doesn't have the transition between the scenes, as I'd like to test the button's usability before having to always quit the application to try the button.
Also, I have declared the majority of objects in the .h file.
Code from MenuScene.m:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//Touch detection declaration
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
touchNode = [self nodeAtPoint:touchLocation];
if([touchNode.name isEqualToString:#"playButton"]){
[playButtonSprite runAction:changePlayButtonTextureON];
}else{}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if ([touchNode.name isEqualToString:#"playButton"]) {
[playButtonSprite runAction:changePlayButtonTextureOFF];
}
}
I would also like to know if there is an alternative method to detecting if the touch is on the play button sprite node. Though, this may be solved when a solution is found for the previous issue I have mentioned.
The best way to handle this would be to subclass SKSpriteNode to handle touches on it's own. You can see such an implementation in this project on GitHub. It cancels the touch when the touch goes out of the node's bounds.
The existing code you have posted is good as the code will not get called when the touch ends out of the play button's bounds.
In your case, since you are handling the touches from the scene itself, you can explicitly set the playButton's texture whenever the touch is detected outside the node's bounds.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
touchNode = [self nodeAtPoint:touchLocation];
if(![touchNode.name isEqualToString:#"playButton"]){
[playButtonSprite runAction:changePlayButtonTextureOFF];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//Touch detection declaration
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
touchNode = [self nodeAtPoint:touchLocation];
if([touchNode.name isEqualToString:#"playButton"]){
[playButtonSprite runAction:changePlayButtonTextureON];
playButtonSprite.isON = YES;
}else{}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (playButtonSprite.isON) {
if ([touchNode.name isEqualToString:#"playButton"]) { // The user really wants to play!
[self startPlaying];
}
[playButtonSprite runAction:changePlayButtonTextureOFF];
playButtonSprite.isON = NO;
}
}
Where changePlayButtonTextureON also sets playButtonSprite.isON to YES, and vise versa.
You will want to make a subclass of SKSpriteNode for playButtonSprite, and add that boolean property isON there.

How to know if an sprite stopped being touched because it moved?

I need to know if the user is touching a sprite. I can easily tell if he lifts his finger or moves his finger out:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"button"]) {
// Sprite stopped being touched
return;
}
}
However I'm clueless about how to know if the touch ends because the sprite moves out while the user keeps his finger in place. I image I should somehow check inside my update method but I don't know how:
// Called before each frame is rendered
-(void)update:(CFTimeInterval)currentTime {
// Check if the user is holding the sprite
}
How can this be done?
I think you are asking for something like this below. There are myriad ways to come at the problem, but here's a basic way to think about it. Depending on the actual complexity of your game, you might need to do some state checking to protect performance, but you get the gist:
//in touchesBegan and touchesMoved update a property:
_lastTouchPoint = //the touch point in scene, etc, blah blah
//if you care about a specific sprite you could also store it:
_someMovingSprite = theSpriteIJustTouchedOrCareAbout;
//in update
if (CGRectContainsPoint(_someMovingSprite.frame, _lastTouchPoint)) {
//the touch is inside the rect of the sprite
}
//OR
NSArray *spritesContainingTheTouchPoint = [self nodesAtPoint:_lastTouchPoint];
if (spritesContainingTheTouchPoint.count == 0) {
//no sprites are being touched
}else{
for (SKSpriteNode *spriteContainingTouch in spritesContainingTheTouchPoint) {
//do something to sprite
}
}
-(void)update:(CFTimeInterval)currentTime {
CGPoint location = [_lastTouchPoint locationInNode:self];
if (!CGRectContainsPoint(_sprite.frame, location)) {
#NSLog(#"Sprite Released");
}
}
Apple has the documentation on every touch event they provide. The method you're looking for is touchesEnded
Handle it the same way you handle touchesMoved and call any methods you need in order to notify the node that it is no longer being touched.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"button"]) {
// Notify the node that the touch has stopped
}
}

Draggable CCNode Cocos2d

I have managed to make a CCNode draggable with the following code:
- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:self];
// start catapult dragging when a touch inside of the catapult arm occurs
if (CGRectContainsPoint([_heroContainer boundingBox], touchLocation))
{
NSLog(#"YUM YUM");
}
_foodNode.position = touchLocation;
}
This works, however if I touch and drag anywhere on the screen it moves the CCNode. How would I make it draggable only if it is being touched?
I assume your touchMoved: method is in the scene itself, this is why you get notified independently from where you touch on the screen (and this is correct).
To move your node only when you touch it, try checking in touchBegan: that you touching your sprite, and set a flag variable, that you'll later check in touchMoved:
Something like this in touchBegan::
if (CGRectContainsPoint(_foodNode.boundingBox, touchLocation))
_shouldDragMyNode = YES;
else
_shouldDragMyNode = NO;
And then in touchMoved: only change position if _shouldDragMyNode is YES:
if (_shouldDragMyNode)
_foodNode.position = touchLocation;
Sorry, can't test the code right now. But something like this should work.

How to detect if Touch input intersects uiimageview

Can't seem to find an answer to this one anywhere.
My game starts when the user touches the screen, there is a path that the finger must stay within, if it touches/intersects the edges then I want it to run the method [self gameover].
The edge will be a UIImageView.
Thanks everyone.
I hope you have overridden the touches moved event. check either the user touch intersects your image view like below.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point =[touch locationInView:self.view];
if (CGRectContainsPoint(self.imageView.frame, point))
{
//Intersects
[self gameOver];
}
}

COCOS2d Creating movement when button is held

I am working on making an iPhone game involving a spaceship moving from left to right on the screen. I want to make it such that the ship only moves if the buttons are pressed. Here is my current code that creates movement, but it doesnt stop when the button is no longer pressed.
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGSize winSize = [CCDirector sharedDirector].winSize;
NSSet *allTouches = [event allTouches];
UITouch *touch = [allTouches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
if (CGRectContainsPoint([_paddle2 boundingBox], location)){
int bottomOfScreenX = 0 + _Paddle1.contentSize.width/2;
id action = [CCMoveTo actionWithDuration:5 position:ccp(bottomOfScreenX,winSize.height/3) ];
[_starShip runAction:action];
[action setTag:1];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
[_starShip stopActionByTag:1];
}
I believe it has to do with your use of "ccTouchesBegan" along with "ccTouchEnded". Notice "touches" versus "touch". They need to be consistent. ccTouchesBegan handles multiple touch events, while ccTouchBegan is meant for a single touch event. So since it appears you are dealing with a single touch event, you really do not need to use ccTouchesBegan. Switch it to ccTouchBegan and you should be fine.
THe problem with this code is that the TouchHasEnded and TouchHasBegan are each using different argument setters. THe Touch began is using an NSSet while the TouchEnded is suing a UITouch. Set the TouchHasEnded as a
-(void)ccTouchEnded:(NSSet *)Touches withEvent:(UIEvent *)event;
or
have the touch began
-(void)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event.
The second method will require a slight tweak to the logic.

Resources