-(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:#"play"]){
NSLog(#"play was touched");
SKScene *mainGameScene = [[MainGame alloc] initWithSize:self.size];
if (!mainGameScene){
SKTransition *transition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.5];
[self.view presentScene:mainGameScene transition:transition];
}
}
}
From my understanding the code above checks if mainGameScene is nil and if it is it then goes through the if statement.
Is this beneficial at all or is it just a waste of code because if the method that this code is in is called multiple times won't it create new objects of SKScene?
It is used to check if mainGameScene is initialized, because you can't presentScene if it is nil.
if you want only to check if object is initialized, then
SKScene *mainGameScene;
//creates new variable - place it at the beginning of the code, before #implementation and #interface
if(mainGameScene){
// it is already inited (you have already tapped once), it will be called always, since it is inited at else
//it is called when you tap the second, the third time, etcetera
SKTransition *transition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.5];
[self.view presentScene:mainGameScene transition:transition];
}else
{
//Calls only once, when you first touching the screen, and initing mainGameScene.
//It won't be called ANYMORE, since it is inited.
mainGameScene = [[MainGame alloc] initWithSize:self.size];
}
The code you have now is useless, as it is trying to do something with object, that isn't initialized.In my case, your mainGameScene won't be reinitialized, once it is inited.
Related
I am creating a simple MainMenu scene and on "play" button select I move to the Game scene as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"play"]) {
SKScene *gameScene = [[GameScene alloc] initWithSize:self.size];
SKTransition *fadeTransition = [SKTransition fadeWithColor:[UIColor blackColor] duration:0.3];
[self.view presentScene:gameScene transition:fadeTransition];
}
}
}
After the transition app crash with EXC_BAD_ACCESS (code=1). I am currently running Xcode6 + SpritKit/Objective-C
Found the problem - apparently caused by particle targetNode assignment:
starParticle.targetNode = self.scene;
which probably should have been released before presenting other scene
I've just started to learn how to program with xCode and more specifically with the new SpriteKit. So bare in mind I'm kind of new to this.
I've managed to move to a different scene by pressing on a label, but somehow the background of my scene has gone all wrong. the background should be like the first scene, but it gave me a background with two horizontal bars. I don't think I've done anything wrong since I've just copied the code needed to set the background to the second scene.
1st scene:
http://imageshack.us/photo/my-images/23/kyud.png/
2nd scene:
http://imageshack.us/photo/my-images/198/472h.png/
my code used to set up both backgrounds:
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"background_start"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.xScale = 0.7;
background.yScale = 0.7;
[self addChild:background];
transition is done with:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"startgame"]){
Options_Scene *gs = [[Options_Scene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:gs transition:doors];
}
}
PS: I find it odd why I have to scale it to 0.7 when the background actually has the dimensions 480x320.
As you are using landscape I would swap the
viewWillAppear
With
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [PMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
I am creating a zoomable and pan-able map for my game (using CCPanZoomController) . Within this map I would like to have a tappable sprite, and when it is tapped, "do something"…
I can get the two things to work perfectly in separate projects, however when I try to combine them, nothing happens when I tap my sprite.
I have included an image to demonstrate further:
//in my init section
self.isTouchEnabled = YES;
mapBase = [CCSprite spriteWithFile:#"MapBase.png"];
mapBase.anchorPoint = ccp(0, 0);
[self addChild:mapBase z:-10];
gym = [CCSprite spriteWithFile:#"Gym.png"];
gym.scale = 0.3;
gym.position = ccp(1620, 250);
[self addChild:gym z:1];
CGRect boundingRect = CGRectMake(0, 0, 2499, 1753);
_controller = [[CCPanZoomController controllerWithNode:self] retain];
_controller.boundingRect = boundingRect;
_controller.zoomOutLimit = _controller.optimalZoomOutLimit;
_controller.zoomInLimit = 2.0f;
[_controller enableWithTouchPriority:1 swallowsTouches:YES];
//end of init
-(void) registerWithTouchDispatcher
{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:NO];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint1 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint1)) return YES;
return NO;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint2 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint2)){
CCLOG(#"SPRITE HAS BEEN TAPPED");
}
}
I want to beable to zoom in/out and pan the wholemap (including the ‘Gym’ sprite).
And if the sprite 'Gym' is tapped by the user, then i would like to “do something”.
If anyone can figure this out, I would be extremely grateful!
Thanks.
quick question (which might be a no-brainer for most here) :)
My code below should draw a circle for every time touch that is recognised but although more than ones touches are sensed only one circle will drawn up at a time.
Can anyone see any obvious issues?
This method sits in the XYZViewControler.m class.
TouchPoint.m is the class that defines the circle.
Thanks a bundle for your help and redirects.
Chris
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *)event {
NSSet * allTouches = [event allTouches]; // get all events
for (UITouch * touch in touches) {
TouchPoint * touchPoint = [[TouchPoint alloc] initWithFrame:CGRectMake(0, 0, circleWidth, circleWidth)];
touchPoint.center = [touch locationInView:[self view]];
touchPoint.color = [UIColor redColor];
touchPoint.backgroundColor = [UIColor whiteColor];
[[self view] addSubview: touchPoint];
[touchPoint release];
CFDictionarySetValue(touchMap, touch , touchPoint);
}
[[self view] setNeedsDisplay];
}
code if fine! One has to enable multitouch for the view in order to make it work!
#property(nonatomic, getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled
I have a drawing app in which I would like to create an undo method. The drawing takes place inside the TouchesMoved: method.
I am trying to create a CGContextRef and push it to the stack OR save it in a context property that can be restored later but am not having any luck. Any advice would be great. Here is what I have ...
UIImageView *drawingSurface;
CGContextRef undoContext;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)];
UIGraphicsPushContext(context);
// also tried but cant figure how to restore it
undoContext = context;
UIGraphicsEndImageContext();
}
Then I have a method triggered by my undo button ...
- (IBAction)restoreUndoImage {
UIGraphicsBeginImageContext(self.view.frame.size);
UIGraphicsPopContext();
drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
When I run this, I believe my drawingSurface is being assigned nil because it just erases everything in the image.
My guess is I can't use pop and push this way. But I can't seem to figure out how to just save the context and then push it back onto the drawingSurface. Hmmmm. Any help would be ... well ... helpfull. Thanks in advance -
And, just for reference, here is what I am doing to draw to the screen, which is working great. This is inside my TouchesMoved:
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)];
CGContextSetLineCap(context, kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
CGContextSetLineWidth(context, self.brush.size); // for size
CGContextSetStrokeColorWithColor (context,[currentColor CGColor]);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
CGContextStrokePath(context);
drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I think you're approaching the problem the wrong way and confusing contexts.
In an immediate mode API you save the 'state' of objects with push/pop, not the graphical representation. The state consists of things like line widths, colours and positions. The graphical representation is the result of a paint operation (a bitmap) and generally something you don't want to save.
Instead try to save 'information' you use to create the drawing.
My initial suggestion would be to decouple your shape creation and painting. On OSX you can use NSBezierPath, but for iOS we have to use an array of points.
For example given this protocol:
// ViewController.h
#protocol DrawSourceProtocol <NSObject>
- (NSArray*)pathsToDraw;
#end
#interface ViewController : UIViewController<DrawSourceProtocol>
#end
You can implement these functions:
// ViewController.m
#interface ViewController () {
NSMutableArray *currentPath;
NSMutableArray *allPaths;
MyView *view_;
}
#end
...
- (void)viewDidLoad {
[super viewDidLoad];
currentPath = [[NSMutableArray alloc] init];
allPaths = [[NSMutableArray alloc] init];
view_ = (MyView*)self.view;
view_.delegate = self;
}
- (NSArray*)pathsToDraw {
// Return the currently draw path too
if (currentPath && currentPath.count) {
NSMutableArray *allPathsPlusCurrent = [[NSMutableArray alloc] initWithArray:allPaths];
[allPathsPlusCurrent addObject:currentPath];
return allPathsPlusCurrent;
}
return allPaths;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
currentPath = [[NSMutableArray alloc] init];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// When a touch ends, save the current path
[allPaths addObject:currentPath];
currentPath = [[NSMutableArray alloc] init];
[view_ setNeedsDisplay];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
// We store the point with the help of NSValue
[currentPath addObject:[NSValue valueWithCGPoint:currentPoint]];
// Update the view
[view_ setNeedsDisplay];
}
Now subclass your view (I call mine MyView here) and implement something like this:
// MyView.h
#import "ViewController.h"
#protocol DrawSourceProtocol;
#interface MyView : UIView {
__weak id<DrawSourceProtocol> delegate_;
}
#property (weak) id<DrawSourceProtocol> delegate;
#end
// MyView.m
#synthesize delegate = delegate_;
...
- (void)drawRect:(CGRect)rect {
NSLog(#"Drawing!");
// Setup a context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
// Get the paths
NSArray *paths = [delegate_ pathsToDraw];
for (NSArray *aPath in paths) {
BOOL firstPoint = TRUE;
for (NSValue *pointValue in aPath) {
CGPoint point = [pointValue CGPointValue];
// Always move to the first point
if (firstPoint) {
CGContextMoveToPoint(context, point.x, point.y);
firstPoint = FALSE;
continue;
}
// Draw a point
CGContextAddLineToPoint(context, point.x, point.y);
}
}
// Stroke!
CGContextStrokePath(context);
}
The only cavet here is that setNeedsDisplay isn't very performant. It's better to use setNeedsDisplayInRect:, see my last post regarding an efficient way of determining the 'drawn' rect.
As for undo? Your undo operation is merely popping the last object from the allPaths array. This exercise I'll leave you to :)
Hope this helps!