I am making an app that will switch to a different screen when it reaches a random number of clicks on the screen. I am using a button that is hidden to segue to the end screen. When I hide the button in the viewDidLoad, it will not appear when called in another method after it reaches the required number of randomized clicks Why does the hidden button not become visible?
-(void)viewDidLoad
{
[super viewDidLoad];
Final.hidden = YES;
SKView * skView = (SKView *)self.view;
skView.showsNodeCount = NO;
SKScene * scene = [BellPepperMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
-(IBAction)GoToNewScreen:(id)sender {
Final.hidden = false;
}
-(void)goToEndScreen{
[self GoToNewScreen:nil];
}
Assuming Final is a UIButton, there are a few things you want to be aware of. First is that you should be setting Final.alpha = 0, and not Final.hidden = YES. This can cause problems with it receiving touch events.
You should also make sure that you have the order of the views setup right. If you want to makes sure that the button is visible you can call [self.view bringSubviewToFront:Final].
Related
I have a small game and I wish to present, in the menu, 2 game modes:
I have the first up and running from my original sprite kit scene and, in order to build my second one, I added another view to the storyboard, created another scene, linked them up just like the originals:
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = NO;
skView.showsNodeCount = NO;
skView.multipleTouchEnabled = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
and linked it to another button in my menu. The problem was, whenever i would press the button to go to the second game mode I would get a SIGABRT Thread 1 error. I made sure everything I linked was declared and valid and still got the error. I then removed the second SKScene (MyScene) and the view was loaded with no problem... just no content.
Is there a specific way I must proceed in order to have another View linked to a SpriteKit game?
I trying to create a game, and following Apple's advice I am using multiple scenes.
You could consider the game to be a text heavy point and click adventure.
Therein lies the problem. Lots of text, having done a bit of a search, it seems the recommend way, or rather the current only way to do this is with a UITextView thus:
[self.view addSubview: textView];
This is all well and good if you only have one SKScene, as that is the scene being displayed by the current view/SKView.
My problem is, that when I add the text to my scene's *view, which isn't the first scene the app loaded (its the third, or higher), the app jumps back to the first scene it loaded (which is my menu scene) and happily displays the required text.
Anybody got any idea why? I have menu scene transitioning to scene one, to scene two (in which I wish to display the test).
Please don't say I need a view per scene if I want to display more than a handful of words per scene, that just doesn't really make sense, but perhaps neither does my usage of SpriteKit.
I am still some what stunned there is no SK equivalent of the UITextView.
Anyway, any help, pointers would be great, thank you.
Ok, here are the relevant parts of the code.... I think.
Controller:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
skView.showsDrawCount = YES;
// Create and configure the scene.
SKScene *scene = [[GTMainMenu alloc] initWithSize:skView.bounds.size];
// Present the scene.
[skView presentScene:scene];
}
where GTMainMenu is a subclass of SKScene, and has a button (orange box) to an "act" (A subclass of GTScene, itself a subclass of SKScene), this will cause a transition to the act, which has another button to the first scene.
Only you never make it to the scene, as the viewDidLoad returns you to the main menu.
This is the viewDidLoad of the scene, which will cause it to "jump" back to the main menu:
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
if (!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(self.size.width/2, self.size.height/2+20, 200, 40)];
textView.center = self.view.center;
textView.textColor = [UIColor blackColor];
textView.font = [UIFont systemFontOfSize:17.0];
textView.backgroundColor = [UIColor whiteColor];
textView.text = #"Where am I?";
[self.view addSubview:textView];
}
There is a git repo available here.
This is a striped down version of my project, removing everything that is unrelated to the issue at hand.
If you will excuse the code, my day job is Java, and I am struggling with certain concepts in Objective C at the moment.
Oh and sorry I managed to include the usersettings :S
Your view controller's viewWillLayoutSubviews method is not safeguarded against repeated execution. This method will not just run at app launch but every time the view rotates and resizes.
Add a check before creating/presenting a scene in that method:
if(self.view.scene == nil) { /* present scene */ }
Have you looked into SKLabelNode? I used it extensively in my SpriteKit game. If you need your SKLabelNode to do anything fancy (physics, etc.), you can just add it to a parent SKSpriteNode.
I have a viewcontroller with an SKview (I'm working with spritekit) inside of it and every time I change the orientation of the phone (from landscape one way to landscape another way or vise versa) it reloads the viewcontroller as if I had just opened the app (the viewcontroller is the initial one).
Is there a work around for it?
I can't seem to find anyone having the same problem.
Code:
http://pastie.org/8669630
You are setting up your SKView in viewWillLayoutSubview which gets called every time device is rotated (view frame changes).
Which is good, because view will have correct dimensions, but you should place some var to know should you set up the view again. For example BOOL sceneSetUp
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
if(!self.sceneSetUp){
// Configure the view.
SKView * skView = (SKView *)self.view;
//skView.showsFPS = YES;
// Create and configure the scene.
SKScene * startScene = [StartViewController sceneWithSize:skView.bounds.size];
startScene.scaleMode = SKSceneScaleModeAspectFill;
SKScene * scene = [gameViewController sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:startScene];
//[skView presentScene:scene];
self.sceneSetUp = YES;
}
}
I'm just trying to set up a basic scene in landscape, with gravity, and having the scene in an edge loop.
I set up the scene's physics body and the mainCharacter sprite physics body, here is my code:
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.backgroundColor = [SKColor redColor];
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:[self frame]]];
}
return self;
}
-(void)setupMain
{
if (!self.mainCharacter)
{
self.mainCharacter = [[SKSpriteNode alloc] initWithImageNamed:#"spriteDefault"];
[self.mainCharacter setPosition:CGPointMake(CGRectGetMidX([self frame]), CGRectGetMidY([self frame]))];
[self addChild:self.mainCharacter];
self.mainCharacter.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.mainCharacter.frame.size];
self.mainCharacter.physicsBody.dynamic = YES;
self.mainCharacter.physicsBody.affectedByGravity = YES;
self.mainCharacter.physicsBody.mass = 0.02;
}
}
So, in portrait mode, everything works perfectly, however, in landscape, things get really screwy.
I figured it has something to do with
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:[self frame]]];
Oddly enough, the edge loop for the x axis for landscape (the y axis in portrait mode) works fine, but I just fall through the y axis (x for portrait).
My guess is that the frame is returning the position on the y axis somewhere not within the bounds of the screen in landscape mode.... meaning its somewhere above or below the screen.
...Maybe... Not really sure.
However, I have tried several different options, including manually setting the rectangle myself by using
CGRectMake()
I wasn't able to get anything to work properly.
Any advice would be greatly appreciated!!!
Okay, this is a super Janky fix...
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 200, 320, 200)];
I'm not sure why this works... and it's obviously going to have some problems on smaller screen sizes... better fixes and explanations would be much appreciated!!
Thanks :D
You will need to setup the edge loop in the viewWillLayoutSubviews, since the scene size is only known at then:
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
}
You need to set the collisionBitMask of your mainCharacter equals to the sceneCategory (need to create). Your nodes will only be affected if you tell them.
I need to add a rain particle effect to my app, I have been having a tough time finding ways to actually execute this idea.
I tried following this CALayer approach tutorial : Link but I am not quite sure if this is the best approach, considering the new iOS 7 SpriteKit Particle Emitter available in Xcode 5.
I have already created the .sks file and it's in my Hierarchy, but I am still unable to add it to my storyboard / project.
With that being said, How exactly do I add a SpriteKit Particle (sks) to my view? I am not at all familiar with scenes, layering , etc in the SpriteKit framework as I am not a game developer.
I need the most details and sample code possible so that I can figure this out please
UPDATE:
I have followed the direction provided in an answer by fellow SO member: AyatollahAndy, please see his answer below. Although I was able to display the SKScene in my view the app crashes when any touch event is received. I get the following:
Thanks
Create a SKScene in your UIView to add a SKEmitterNode particle effect.
One way of doing this:
1.In storyboard (or programatically if you prefer) add a View object on top of the existing View and resize it to your needs.
2.Change the class of the new view to SKView
3.In your view controller .h file create a property for the SKView:
#property IBOutlet SKView *skView;
4.Link the SKView on your storyboard to the skView property.
5.Create a new class, subclassing SKScene. MyScene.h will look like:
#import <SpriteKit/SpriteKit.h>
#interface MyScene : SKScene
#end
MyScene.m below contains code to create a particle effect whenever and wherever the SKView is touched.
#import "MyScene.h"
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
myLabel.text = #"Hello, World!";
myLabel.fontSize = 30;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:myLabel];
}
return self;
}
//particle explosion - uses MyParticle.sks
- (SKEmitterNode *) newExplosion: (float)posX : (float) posy
{
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"MyParticle" ofType:#"sks"]];
emitter.position = CGPointMake(posX,posy);
emitter.name = #"explosion";
emitter.targetNode = self.scene;
emitter.numParticlesToEmit = 1000;
emitter.zPosition=2.0;
return emitter;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
//add effect at touch location
[self addChild:[self newExplosion:location.x : location.y]];
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
6.In your main view controller, include your scene class:
#import "MyScene.h"
and add code to viewDidLoad to initialise the SKView:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the SKView
SKView * skView = _skView;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
You should then have a working SKScene within your main UIView.
In modern Xcode:
This is now very easy.
1. In Xcode, click to create a new
"SpriteKit Particle File"
it will be a single .sks file.
(NOTE: Do NOT choose "SceneKit Particle System File". Choose "SpriteKit Particle File".)
Click once on the .sks file. Notice the many controls on the right.
The particles will actually be moving, it is a living preview. Anything that can be done with particles, you can do it. It is like using particles in a game engine, except performance is 18 billion times better.
2. Have any ordinary UIView, anywhere you want:
#IBOutlet weak var teste: UIView! // totally ordinary UIView
3. Just use the following code to link:
The following slab of code will put your new particle system, inside, the ordinary UIView "teste":
import SpriteKit ...
let sk: SKView = SKView()
sk.frame = teste.bounds
sk.backgroundColor = .clear
teste.addSubview(sk)
let scene: SKScene = SKScene(size: teste.bounds.size)
scene.scaleMode = .aspectFit
scene.backgroundColor = .clear
let en = SKEmitterNode(fileNamed: "SimpleSpark.sks")
en?.position = sk.center
scene.addChild(en!)
sk.presentScene(scene)
Add this to anything you want.
If you want a sparkling button, add it to a button.
If you want the whole screen to shower rainbows, add it to a full-screen view.
It's that easy.
Example of how to use the SpriteKit Particle File controls:
Say you want a burst of sparks, which ends.
Set the max to 50...
Tip - if your effect "finishes" (ie, it is not a loop), it seems you can simply get rid of the SKScene when finished. Like this:
...
scene.addChild(en!)
sk.presentScene(scene)
delay(1.5) { sk.removeFromSuperview() }
That one line of code at the end seems to clean-up everything.
BTW if you want fantastic ideas for particle systems, a great idea is click to the Unity "asset store", where various particle artists buy and sell particle systems. Their work will give you great ideas.
Just click "particles" in the list on the right; watch the videos. (Innovative examples .)
Note! Apple are going to make it so that you can very simply make a SKView in storyboard, and select the .sks scene. However ..
... it does not work yet! It's still broken as of the last edit to this post (2020). So you need the code fragment above.
You can add SKView as a subview within your UIKit hierarchy. A function like the following would work, allowing you to create a UIImageView with the effect as a subview, and then you can add this to your main view. Be sure to link against SpriteKit.
UIImageView *NewEffectUIImageViewWithFrame(CGRect frame)
{
UIImageView *tempView = [[UIImageView alloc] initWithFrame:frame];
SKView *skView = [[SKView alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
[tempView addSubview:skView];
SKScene *skScene = [SKScene sceneWithSize:skView.frame.size];
skScene.scaleMode = SKSceneScaleModeAspectFill;
skScene.backgroundColor = [UIColor clearColor];
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"SparkParticle" ofType:#"sks"]];
emitter.position = CGPointMake(frame.size.width*0.5,0.0);
[skScene addChild:emitter];
[skView presentScene:skScene];
return tempView;
}
In the end, if all you need is an emitter, it may be easier to create a CAEmitterLayer and add that as a sublayer to your UIView instead. Of course, that means you have to programmatically create the CAEmitterLayer and can't use the cool Xcode particle editor...
Here's approach totally different approach to try. My buddy gave me this cool way to go. Using CAEmitterCell. All in code! Looks like you need a spark.png image.
extension UIView {
final public func ignite() {
let emitter = CAEmitterLayer()
emitter.frame = self.bounds
emitter.renderMode = kCAEmitterLayerAdditive
emitter.emitterPosition = self.center
self.layer.addSublayer(emitter)
let cell = CAEmitterCell()
let bundle = Bundle.init(for: UIColor.self)
let image = UIImage(named: "spark", in: bundle, compatibleWith: traitCollection)
cell.contents = image?.cgImage
cell.birthRate = 1500
cell.lifetime = 5.0
cell.color = UIColor(red: 1.0, green: 0.5, blue: 0.1, alpha: 1).cgColor
cell.alphaSpeed = -0.4
cell.velocity = 50
cell.velocityRange = 250
cell.emissionRange = CGFloat.pi * 2.0
emitter.emitterCells = [cell]
}
}
Enjoy.
Actually there is a way to add particles without SpriteKit - CoreAnimation's CAEmitterCells.
This way you can add particles in your UIView easily. If you want to play around with the parameters and get the code easily, get this app (Particle X).
It also supports SpriteKit so if you want to play around or design particles on the go and immediately get the code for it, this app is the solution.
PS. If you haven't noticed it, I am the developer of the app - made it to use it myself when designing app and games. :)
Putting this here for visibility reasons.
The answers regarding the user of a .clear backgroundColor are correct, except that you must also set the allowsTransparency property on SKView to 'true'.
skView.allowsTransparency = true
skView.backgroundColor = .clear // (not nil)
scene.backgroundColor = .clear
If you don't set allowsTransparency to true, and you layout your SKView over, say, a UIImageView, the composition engine will have a fit, and will send your GPU red-lining, even if only a single particle is drawn. (In the Simulator, the CPU will spike instead.)
You cannot use particle effects within UIView directly.
SKEmitterNode must be in a node tree defined with a node scene (SKScene). The scene node runs an animation loop that renders the contents of the node tree for display. UIView is static, won't work for it.
However, you probably able to create a scene inside your UIView, but I've never tried to do that.