I'm trying to call my newExplosion method (in a touchesBegan method) that is in my Scene class from my ViewController.m class. The problem is that it doesn't add the SKEmitterNode to the View (which is what the newExplosion method is supposed to do). It works when the touchesBegan method is in the Scene class, but not when it is called from my ViewController class.
This is my Scene class:
#import "Scene.h"
#import <SpriteKit/SpriteKit.h>
#implementation Scene : SKScene
- (void) newExplosion: (float)x : (float) y {
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"crouchEffect" ofType:#"sks"]];
emitter.position = CGPointMake(x,y);
[self addChild:emitter];
}
// This works VVVV
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self newExplosion:100 :100];
}
This is the SpriteKit related stuff in my ViewController.m class:
- (void)viewDidLoad{
SKView * skView = _skView;
SKScene * scene = [Scene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
// Doesn't work VVVV
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
Scene * ss = [[Scene alloc] init];
[ss newExplosion:100:100];
}
I added a debug message in my newExplosion method to see if it was actually running it and it printed it out. So it's being called, but not adding the SKEmitterNode to the view.
You are creating a new scene everytime you touch the screen, but you never add this to the view, I think you want to do something like this
//In view controller interface add
{
Scene * _scene;
}
//In implementation, change the following
- (void)viewDidLoad{
SKView * skView = _skView;
_scene = [Scene sceneWithSize:skView.bounds.size];
_scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:_scene];
}
// Doesn't work VVVV
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[_scene newExplosion:100:100];
}
Related
How can I get the bound of scene?
I'm testing with demo and I initialize scene in this way:
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
So, when I tap on screen I log:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode: self];
NSLog(#"%f, %f", location.x, location.y);
}
}
When I press left-bottom I get circa 300-0 while I'm expecting 0-0.
How can I get the top-bottom-left-right bounds? I can't find a method like:
[scene getBounds]
Scene.frame prints: (0,0,1024,768) that not corresponds!!
You need to set the size of the GameScene to be equal to the size of its SKView. You can either do this in the didMoveToView of your GameScene:
-(void)didMoveToView:(SKView *)view {
[super didMoveToView: view];
self.size = view.bounds.size;
}
Or, you can set the size when you instantiate your GameScene:
SKView * skView = (SKView *)self.view;
// ...
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
scene.size = skView.bounds.size;
[skView presentScene:scene];
Initialize scene like this:
GameScene *scene = [GameScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
Test coordinates like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
/* Pressed node */
SKNode *node = [self nodeAtPoint:location];
NSLog(#"x - %f; y - %f", location.x, location.y);
}
Please help me understand the following behavior (iOS Sprite Kit).
The output of the code I present below is:
1.skView.bounds.size = 768.00, 1024.00
2.skView.bounds.size = 1024.00, 768.00
As seen above, width and height are switched between the two methods, and that causes my second scene to be presented not in the correct scale.
My game will run in landscape mode only, which means that in fact, the second width x height ratio is the correct one (though it is the first that renders the scene in the correct aspect ratio, which in itself is a mystery to me).
Can anyone tell me how I can fix this behavior? What am I doing wrong?
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentGameState = GS_INTRO_SCENE;
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
printf("1.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *scene = [SKTGameIntroScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.currentGameState == GS_INTRO_SCENE) {
self.currentGameState = GS_GAME_PLAY;
SKView * skView = (SKView *)self.view;
printf("2.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *nextScene = [SKTMyScene sceneWithSize:skView.bounds.size];
nextScene.scaleMode = SKSceneScaleModeAspectFill;
SKTransition *fadeIn = [SKTransition fadeWithDuration:5.0f];
[skView presentScene:nextScene transition:fadeIn];
}
}
I greatly appreciate it in advance.
** edit: **
#giorashc solved my problem suggesting I move the scene initiation to -(void)viewWillLayoutSubviews method.
But my second scene remains stretched.... here is the full code for SKTViewController.m after the change (can anyone say why second view is still stretched?):
//
// SKTViewController.m
// SpriteKitTest
//
// Created by Claudia Dazcal on 13/07/14.
// Copyright (c) 2014 DazcalFamily_inc. All rights reserved.
//
#import "SKTViewController.h"
#import "SKTMyScene.h"
#include "SKTGameIntroScene.h"
typedef enum
{
GS_INTRO_SCENE,
GS_GAME_PLAY
}GameStates;
#interface SKTViewController ()
#property GameStates currentGameState;
#property BOOL firstSceneLoaded;
#end
#implementation SKTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentGameState = GS_INTRO_SCENE;
self.firstSceneLoaded = NO;
}
-(void)viewWillLayoutSubviews
{
if (!self.firstSceneLoaded) {
self.firstSceneLoaded = YES;
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
printf("1.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
SKScene *scene = [SKTGameIntroScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.currentGameState == GS_INTRO_SCENE) {
self.currentGameState = GS_GAME_PLAY;
SKView * skView = (SKView *)self.view;
printf("2.skView.bounds.size = %.2f, %.2f\n", skView.bounds.size.width, skView.bounds.size.height);
//CGSize DebugSize = CGSizeMake(skView.bounds.size.height, skView.bounds.size.width);
//SKScene *nextScene = [SKTMyScene sceneWithSize:DebugSize];
SKScene *nextScene = [SKTMyScene sceneWithSize:skView.bounds.size];
nextScene.scaleMode = SKSceneScaleModeAspectFill;
//SKTransition *fadeIn = [SKTransition fadeWithDuration:5.0f];
//[skView presentScene:nextScene transition:fadeIn];
[skView presentScene:nextScene];
}
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end
* edit 2 **
Ok, it`s all sorted out now.
#giorashc solved my problem right away when he suggested moving the scene initiation to viewWillLayoutSubviews. It turns out that since I was previously working with the wrong bounds, I set up my original scene with the wrong values and now that it was fixed, it looked stretched.
But it`s fixed now. Thank you so much #giorashc!!
Initialize your scene in the viewWillLayoutSubviews method. In this method the view bounds are updated correctly.
Make sure though that you initialize the scene only once as this method is called whenever the view controller presents another view controller or the application is changing its orientation.
In the first method the view hasn't rotated to landscape mode yet. In the second it has rotated to landscape mode so is now reporting bounds that are wider than it is tall.
I have doubts about touch event in cocos2d, in particular:
I have two layers:
GameLayer.m
-(id)init
{
if(self=[super init])
{
NSLog(#"GAME");
CCSprite*game=[CCSprite spriteWithFile:#"alien.png"];
CGSize size=[CCDirector sharedDirector].winSize;
self.contentSize=CGSizeMake(50,50);
self.touchEnabled=YES;
game.position=CGPointMake(40,40);
[self addChild:game];
//NSLog(#"%d",[[self children] count]);
//NSLog(#"%f",self.position.x);
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"GAME");
}
UserInterfaceLayer.m
-(id)init
{
if(self=[super init])
{
NSLog(#"USER");
self.touchEnabled=YES;
CCSprite*game=[CCSprite spriteWithFile:#"spiders.png"];
CGSize size=[CCDirector sharedDirector].winSize;
self.contentSize=CGSizeMake(20,20);
game.position=CGPointMake(40,40);
[self addChild:game];
//NSLog(#"%d",[[self children] count]);
//NSLog(#"%f",self.position.x);
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"USER");
}
Both are children of one scene:
MultiLayerScene.m
-(id)init
{
if(self=[super init])
{
sharedMultiLayerScene = self;
self.contentSize=CGSizeMake(30, 30);
CGSize size=[CCDirector sharedDirector].winSize;
// The GameLayer will be moved, rotated and scaled independently
GameLayer* gameLayer = [GameLayer node];
gameLayer.position=CGPointMake(250,250);
[self addChild:gameLayer z:1 tag:1];
//gamelayerposition = gameLayer.position;
// The UserInterfaceLayer remains static and relative to the screen area.
UserInterfaceLayer* uiLayer = [UserInterfaceLayer node];
uiLayer.position=CGPointMake(-100, -100);
[self addChild:uiLayer z:-1 tag:2];
}
return self;
}
My problem is that I touch on screen at any position their ccTouchesBegan event run.
For example if I touch only on GameLayer runs both the GameLayer touches event and the other.
I try also to insert the two layer in two different position(as you can see from the code) , but the problem still persist.
How Do I resolve this problem?
I would like ,for example,if I touch on Gamelayer respond only its touch event.
Try this
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
Priority: lower number = higher priority.
I'm currently learning Cocos2D and I can't figure out how to access my sprite '_imgStudio' to make it universal so I can clean it up the image after my "removeStudio" timer finishes. I'd like to be able to move both the CCSprite draw functions into the spawnStudio function and to kill it once the timer finishes running.
However, I get an error saying 'Use of undeclared identifier '_imgStudio' so my removeStudio timer right now is pretty much useless.
Here's part of my helloWorldFile.
#implementation GameEngine
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameEngine *layer = [GameEngine node];
[scene addChild: layer];
return scene;
}
#pragma mark Main Initializer
//Initalizes GameSetup
-(id) init
{
if( (self=[super init]) ) {
NSLog(#"Game Setup Worked!");
CGSize winSize = [CCDirector sharedDirector].winSize;
CCSprite *_imgStudio = [CCSprite spriteWithFile:#"Studio~ipad.png"];
_imgStudio.opacity = 0;
_imgStudio.position = ccp(winSize.width/2, winSize.height/2);
[_imgStudio runAction:[CCFadeTo actionWithDuration:2.5f opacity:255]];
[self addChild:_imgStudio];
[self scheduleOnce: #selector(spawnStudio:) delay:10];
[self scheduleOnce: #selector(removeStudio:) delay:20];
}
return self;
}
-(void) spawnStudio: (ccTime) dt {
NSLog(#"TEST");
}
-(void) removeStudio: (ccTime) dt {
[_imgStudio removeFromParentAndCleanup:YES];
NSLog(#"ImageRemoved");
}
Thanks!
*Working Code Now*
OK, I thought that this would be easy to get working but it turns out to not work like I expected.
I am trying to get the Touch location from a CCLayer that can be moved or zoomed, not the location on the screen itself? here is how I thought that it would work but it crashes?
Interface
#import "cocos2d.h"
#interface TestTouch : CCLayer {
CCLayerColor *layer;
}
+(CCScene *) scene;
#end
Implementation
#import "TestTouch.h"
#implementation TestTouch
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
TestTouch *layer = [TestTouch node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
- (id)init
{
self = [super init];
if (self) {
CGSize winsize = [[CCDirector sharedDirector]winSize];
CCLayerColor *layer = [CCLayerColor layerWithColor:ccc4(255, 255, 255, 255)];
// layer.scale = 0.7f;
layer.contentSize = CGSizeMake(640, 960);
layer.position = CGPointMake(winsize.width/2, winsize.height/2);
layer.isRelativeAnchorPoint = YES;
[self addChild:layer z:0];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:layer priority:0 swallowsTouches:YES];
}
return self;
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchStart = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
touchStart = [layer convertToNodeSpace:touchStart];
NSLog(#"Touch:%f,%f",touchStart.x, touchStart.y);
return YES;
}
#end
If I change this line to include "self":
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
It will obviously work but then I get the location relevant to the screen and not the layer, which is what I need.
You need to convert the location on the screen to the layer's "node space":
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchStart = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
// convert touch location to layer space
touchStart = [self convertToNodeSpace:touchStart];
NSLog(#"Touch:%f,%f",touchStart.x, touchStart.y);
return YES;
}