I am trying to implement an on/off button in Sprite Kit for the music/sound effects. Here is the code to set up the buttons:
-(void)setUpSoundIcon{
if (soundOrNoSound == YES) {
soundIcon = [SKSpriteNode spriteNodeWithImageNamed:[self imageNamed:#"sound"]];
sound = 2;
}else if (soundOrNoSound == NO) {
soundIcon = [SKSpriteNode spriteNodeWithImageNamed:[self imageNamed:#"nosound"]];
sound = 1;
}
soundIcon.name = #"Sound";
if (screenWidth == 1024 && screenHeight == 768) {
soundIcon.position = CGPointMake(screenWidth - 50, 50);
}else if(screenHeight == 320){
soundIcon.position = CGPointMake(screenWidth - 30, 30);
}
[soundIcon setZPosition:2000];
[self addChild:soundIcon];
}
Then in the touchesBegan method I have the sound icon image change to represent the music on or off. So, my problem is that I have the background music playing correctly, I just need a way to see if the user pressed the sound icon to be off, and then making sure the music and sound effects do not play unless the user pressed the sound icon on. I need a way to do it so that it works between multiple classes too. Thank you!
This is how I would do it. In touches began I would check if your On/Off button is tapped. Then I would toggle the button and change the BOOL variable for playing the sound and then start/stop music that is playing.
It would be great for you to have separate class for sound/music which will have that BOOL variable and methods for playing/pausing music and other music related stuff. But you can also store that values in the scene or anywhere else, but the playing part would be great to have in separate class.
And I would to it so you have 2 sprites on the scene, one would be soundOnIcon other soundOffIcon and you can make them hidden when they should not be visible
Here is a bit of code that I would do:
-(void)toggleMusicSound
{
if([MusicPlayingClass soundOn])
{
[MusicPlayingClass setSoundOn:NO];
[MusicPlayingClass stopAllSounds];//this method will stop playing all continuous music and set other music logic if you want
[soundOnIcon setHidden:YES];
[soundOffIcon setHidden:NO];
}
else
{
[MusicPlayingClass setSoundOn:YES];
[MusicPlayingClass playSounds];//it will start playing continuous music if it has to, and set other music logic if you want
[soundOnIcon setHidden:NO];
[soundOffIcon setHidden:YES];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
//check if your button is tapped
if(CGRectContainsPoint(soundIcon.frame, positionInScene))
{
[self toggleMusicSound];
}
}
That is how I would do it. Maybe it helps, but if you need some more tips or explanation I would be happy to help. :)
Related
I have two SKNodes, RMnode and RLnode, that receive touches. Unfortunately, at the same time.
I tried disabling the touch on the 2nd SKnode, when 1st has been touched and vice versa, but for some reason it doesn't seem to work.
Is there perhaps another approach to this?
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint Rubyposition = [touch locationInNode:self];
[self selectNodeForTouch:Rubyposition]; //a helper method that asks the scene (self) for the node that is on the position touchLocation.
SKNode *RMnode = [self nodeAtPoint:Rubyposition];
SKNode *RLnode = [self nodeAtPoint:Rubyposition];
if ([RLnode.name isEqualToString:#"Ruby1"]) {
if(_TouchOnRubyRL == NO){
_TouchOnRubyRL = YES;
//RMnode.userInteractionEnabled = NO; //Not working
[self.level ActivatedBricks:_TouchOnRubyRL];
}
else if(_TouchOnRubyRL == YES){
_TouchOnRubyRL = NO;
//RMnode.userInteractionEnabled = YES; //not working
[self.level ActivatedBricks:_TouchOnRubyRL];
}
}
if ([RMnode.name isEqualToString:#"Ruby2"]) {
if(_TouchOnRubyRM == NO){
_TouchOnRubyRM = YES;
//RLnode.userInteractionEnabled = NO; //Not working
[self.level ActivatedBricks:_TouchOnRubyRM];
}
else if(_TouchOnRubyRM == YES){
_TouchOnRubyRM = NO;
//RLnode.userInteractionEnabled = YES; //Not working
[self.level ActivatedBricks:_TouchOnRubyRM];
}
}
}
}
The reason setting the node's userInteractionEnabled is not working as you expect is that your touchesBegan is in the scene, not the sprite. userInteractionEnabled only allows/disallows touches to the SKNode it is a property of (an SKScene is an SKNode too) - and in your code the scene's userInteractionEnabled is always true so the touch always gets through to the scene's touchesBegan method (your sprite's touchesBegan is being disabled - but you are not overriding it so you don't notice).
To use userInteractionEnabled in this way you would need to subclass the sprites themselves and handle touches within the subclassed sprite - then setting userInteractionEnabled will work as you expect.
If you do not want to refactor your code like that, you could mark each sprite as 'untouchable' in some other way (this SO answer suggests using the SKNode's existing zPosition property) and then use this in your scene before accepting the touch (e.g. if touchedNode.zPosition != 0 ...).
In my app setup, I have a navigation controller with 4 ImageViews. 1 of them can be dragged around, while the other 3 are stationary at the top section of the view. Using the code below, I have it set up so that the user drags the one image view to the image view of where he wants to go. So to get to view 1, he drags the movable image view to image view 1, and so on. The issue is that with the width of the image views, it is possible for the selector view to touch two at one time, which creates a nesting view controller issue. Is there a way I can keep this from happening, short of moving the image views so far away that it is impossible for more than one to be selected at a time?
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
// If the touch was in the placardView, move the placardView to its location
if ([touch view] == clock) {
CGPoint location = [touch locationInView:self.tabBarController.view];
clock.center = location;
BOOL isIntersecting = CGRectIntersectsRect(clock.frame, prayer.frame);
BOOL isIntersecting2 = CGRectIntersectsRect(clock.frame, fasting.frame);
BOOL isIntersecting3 = CGRectIntersectsRect(clock.frame, study.frame);
if(isIntersecting){
[self schedulePrayer];
NSLog(#"prayer");
}
if(isIntersecting2){
[self scheduleFasting];
NSLog(#"fasting");
}
if(isIntersecting3){
[self scheduleStudying];
NSLog(#"Studying");
}
return;
}
}
Why don't you just use if ... else if ... else if?
if(isIntersecting){
[self schedulePrayer];
NSLog(#"prayer");
}
else if(isIntersecting2){
[self scheduleFasting];
NSLog(#"fasting");
}
else if(isIntersecting3){
[self scheduleStudying];
NSLog(#"Studying");
}
Then, only one will be triggered at a time.
Create another BOOL "isTouching" and make it global. Then inside your if(isIntersecting) set "isTouching" to global, and add "isTouching" as a condition such that:
if ([touch view] == clock && (!isTouching))
You also need to set isTouching to false in the case that the UIImageView is not on any of the intersecting views and you should be good to go :)
That should be enough hints for you to solve your problem, but if you'd like more clarification let me know.
So I added a pause button to my game and the freezing and unfreezing of the view works. However I want it to display a message saying "PAUSE" over the screen if the view is currently frozen. But if I'm touching my pause button it doesn't display the label. It's really strange, because I discovered that if I'm turning my device to landscape mode, the "PAUSE" message appears. (and vice versa too, so if I touch the button in landscape mode and turn my device to portrait, the pause label appears)
Can anyone figure this out?
BTW: I also tried out different methods to do this, like initializing the pause label as soon as the scene starts and hide/reveal it whenever needed. This didn't work either.
I also tried to do it with an if-statement in the update method ( if the scene is paused => reveal the pause label) this didn't work either.
I asked this question before on here and the apple devforums but no one could solve this problem yet. Someone thought I would present the scene in viewwilllayoutsubviews, which would explain the portrait-landscape behavior, but I never used that method in my whole application code.
-(void)gameScene{
//Pause
pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pause.jpg"];
pauseButton.position = CGPointMake(screenWidth/2, screenHeight-80);
pauseButton.zPosition = 5;
pauseButton.name = #"pauseButton";
pauseButton.size = CGSizeMake(80, 80);
[self addChild:pauseButton];
//Pause Label (wenn Pause gedrückt wird)
pauseLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial"];
pauseLabel.text = #"PAUSE";
pauseLabel.fontSize = 70;
pauseLabel.position = CGPointMake(screenWidth/2, screenHeight/2);
pauseLabel.zPosition = 5;
pauseCount = 1;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//Pause Button
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"pauseButton"]) {
pauseCount++;
if (pauseCount%2 == 0) {
self.scene.view.paused = YES;
[self addChild:pauseLabel];
}
else{
self.scene.view.paused = NO;
[pauseLabel runAction:remove];
}
}
}
I had a similar problem with pausing and found that the problem related to the fact that although the pauseLabel was getting added to the node tree the scene was pausing before it was displayed. The solution I used was to use SKAction to run the label add and pause in sequence.
SKAction *pauseLabelAction = [SKAction runBlock:^{
[[self scene] addChild:pauseLabel];
}];
SKAction *delayAction = [SKAction waitForDuration:0.1]; // Might not be needed.
SKAction *pauseSceneAction = [SKAction runBlock:^{
[[[self scene] view] setPause:YES];
}];
SKAction *sequence = [SKAction sequence:#[pauseLabelAction, delayAction, pauseSceneAction]];
[self runAction:sequence];
I have not tested this but there might also need to be a small delay in there to give the label time to display before the pause is fired.
I'm trying to create a program in which when I tap in roughly the same location as where my last touch ended, a circle changes color. I've tried using the previousLocationInView method using logic along the lines of: "if touchesbegan location == previousTouchesLocation, change color to blue." This method didn't work because the touchesBegan and previousTouchLocation coordinates have the same value which always changed the color whenever I tapped the screen regardless of the location. I get the same result if I substitute previousTouchLocation with touchesEnded. Here is the code if anyone thinks that it could help.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
touchLocation = [touch locationInView:self.view];
lastTouchLocation = [[touches anyObject] previousLocationInView:self.view];
distance_x = touchLocation.x - cvx;
distance_y = cvy - touchLocation.y;
previousDistance_x = lastTouchLocation.x - cvx;
previousDistance_y = cvy - lastTouchLocation.y;
if ((previousDistance_x == distance_x) && (previousDistance_y == distance_y)) {
if (([cv.color isEqual:[UIColor redColor]])) {
[cv setColor:[UIColor blueColor]];
}
else
[cv setColor:[UIColor redColor]];
}
}
-previousLocationInView: gives you the previous location of a touch that's currently moving on the screen. It doesn't give you the location of a different, now ended, touch.
What you're going to have to do is, on the first touch, store the touch's location in a property or ivar. Then, on the second touch, compare its location to the stored location. You'll need to allow for some leeway—fingers are not pixel-accurate in the way mice can be—so a touch that's moved ten or twenty pixels should probably count as a double-tap.
Actually, come to think of it, your best bet might be to use a UITapGestureRecognizer with numberOfTapsRequired set to 2. This will handle all the detection logic for you, and you can combine it with other gesture recognizers to build up more complex behaviors.
I'm trying to play a sound on touchesEnded but I'm having a problem. There are multiple objects that get moved around, so if the below code holds true when any object is moved, it constantly plays the sound. How do I only play it once?
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if(CGRectContainsRect([image1 frame], [image2 frame])){
[self playsound];
}
}
If you only want it to play for a certain object that calls touchesEnded, you first need to identify that object and then you can just do a quick if-then statement.
If you only want it to play once, then just give it a quick variable like int playCount = 0; and then set it to playCount = 1; after you're done playing and do an if-then statement on that as well (i.e. play it if playCount is 0, don't play it if playCount is 1).