I have just started learning Cocos2d using the great tutorials
Ray Wenderlich has produced. However I'm getting to a point where my HelloWorldScene main file is getting a little large.
I would like to be able to sort sprite generation methods into one file, misc. methods into another and so on, leaving me with the core level files containing the different scenes and init methods.
Is it possible to copy methods over to a new .m file and bring them into the HelloWorldScene.m when I need them?
How do you organize your game files?
I highly recommend layering your game. It keeps it organised and neater, reducing the amount of code in any one file.
Within cocos2d, there are 3 classes you should take note of to achieve this:
CCDirector, CCScene, CCLayer.
What you can do is, create multiple layers within a single scene (GameScene).Eg:
Have a HUD, Game Interface, Background and Foreground.
These can all be split into their separate CCLayers and added together within the CCScene.
So now you have split HelloWorld.m into:
GameScene.m
HudLayer.m
GameInterfaceLayer.m
Background.m
Forground.m
After importing all the h files into your GameScene.h, You can then add each Layer to the scene as Follows:
#implementation GameScene
+(CCScene){
HudLayer *hud = [HudLayer node];
[self addChild: hud z:3];
GameInterfaceLayer *game = [GameInterfaceLayer node];
[self addChild: game z:1];
...} etc
#end
Once you do this, if you dont change your AppDelegate, your CCDirector will give you an error. CCDirector basically dictates which Scene is loaded.
Change this to point to your GameScene.
You can visualise: the Director of the Movie, Filming a Scene, filled with many Layers.
Related
I have two questions.
What is the proper method to transfer the physical art I make on paper to a computer so that it utilized as SpriteKit?
What is the proper method to create Sprites on a Mac to be utilized in Xcode7?
This is generally covered in the Working With Sprites documentation. Here's a high-level overview though.
You want to create a "textured sprite" which is "the primary way that you bring custom artwork into a scene".
"This custom artwork might represent characters in your game, background elements, or even user interface elements, but the basic strategy is the same. An artist creates the images, and your game loads them as textures. Then you create sprites with those textures and add them to the scene."
Or in other words, scan your drawings in using any software. Touch them up and make the background transparent (if you want) using image editing software like Photoshop or Pixelmator. Export the result as a PNG file. Drag this file into your Xcode library to import the file into your project.
Then, using the filename, just make an SKSpriteNode object. Here's the Objective-C code from the docs:
SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithImageNamed:#"rocket.png"];
spaceship.position = CGPointMake(100,100);
[self addChild: spaceship];
Here's the same code in Swift:
let spaceship = SKSpriteNode(imageNamed: "rocket.png")
spaceship.position = CGPoint(x: 100, y: 100)
addChild(spaceship)
I recommend reading that entire document if you have more questions; it's very thorough.
What I want to do:
I want to use sprite sheets to load all my enemies in the game. They would have to be removed once they are either destroyed by the good guy or when they go off screen. I have 6-7 enemies some of which are animations. I will be reusing them multiple times. I wan to load and unload them effectively from the memory.
What I am doing:
I first load the spritesheets:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"Obstacles.plist"];
CCSpriteBatchNode *obstaclesspriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"Obstacles.pvr.ccz"];
[self addChild:obstaclesspriteSheet];
I have a class called BadBoys which handles the bad guys. Every time I want to create a bad guy I create an instance of the class. Inside the class I create the sprite and add it to the layer.
baddies[x] = [[BadBoys alloc] init];
//INSIDE THE CLASS
baddie = [CCSprite spriteWithSpriteFrameName:#"cannonball-hd.png"];
I remove the sprite from the layer when it gets destroyed and then release the instance of the class.
[self removeChild:[baddies[x] getBaddie] cleanup:YES]; //getBaddie returns the sprite
[baddies[x] release];
I know this is a good way to do it. What I want to know is if this is the most efficient way of doing this? I thought of another way of loading the image:
Load the image asynchronously to the CCTextureCache.
Then create a sprite using the texture from the cache.
Add it to a NSMutableArray which will hold all enemies that are alive
Then when I dont need it anymore I can destroy it the following way:
CCTexture2D * texture = spriteName.texture;
[spriteName.parent removeChild:spriteName cleanup:YES];
[[CCTextureCache sharedTextureCache] removeTexture:texture];
[backgroundSprites removeObject:spriteName];
Is this a better method? Please share your views and suggestions. Thanks.
What you're doing is good, as you said.
Your alternative is a terrible idea. I'll tell you why: you will want to avoid loading textures into memory and removing them from memory frequently during gameplay. Loading a texture from flash memory is slow. Removing the texture from memory also takes a little time, and is rather pointless if you have enough free memory available anyway.
Furthermore if you're using a spritesheet and you're removing one obstacle but try to remove the obstacle's texture (which is Obstacles.pvr.ccz) from memory as well, then the texture cache won't remove the texture from memory anyway. Because it's still being used by the spritesheet.
Lastly: premature optimization is the root of all evil.
I am trying to plot few points dynamically on my Custom View using Quartz functions so that i get a complete graph. I handle drawing of lines inside the drawRect method of Custom View where I get the current context and draw the lines. But, they get erased when i try to draw a new line. I want to have those lines also visible along with the new ones drawn. Please let me know how to do this. I can't store all the points together and draw at the end. I want to continously update my view. Thanks in advance.
Add a method to your custom view:
- (BOOL) isOpaque { return YES; }
That will prevent the drawing of any views behind yours including the background.
Note, however, that on resize you'll need to redraw everything either way. A more proper solution would be to use off-screen image to draw into instead.
You could use CALayers: add a new child layer to the root each time you have new data, and draw to that layer. Your drawing code can remain the same: you just need to put in the code for creating and using layers, which is actually pretty easy.
See: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004514
This is probably something quite basic. I am trying to implement AdWhirl into my app, which I have done successfully for the technical part. When I load my app, the add loads and then slides down from the top to sit at the bottom of the screen. However, when I rotate the device, the advert uses "precise" locations and moves off screen. When the advert reloads (refreshes every 15 seconds) the advert moves up to the bottom of the screen of the landscape window. Again, when rotating back from landscape, the Advert Aligns it's self in the middle of the page vertically (covering content) until a new advert loads. I have attached a number of photos, in a series showing what happens, all in order and taken at least 10 seconds apart (showing test advert of "Hello").
My code from the Implementation file is included at the end of this post - sorry for not using the code format, just didn't want to put spaces in front of the whole block, and I think it's all relatively relevant. It's also available at the paste bin: http://pastebin.com/mzavbj2L
Sam
Sorry - it wouldn't let me upload images. Please send me a PM for images.
I recommend handling the rotation in the willRotateToInterfaceOrientation:duration: method or the didRotateFromInterfaceOrientation:method. You will be able to determine what your new orientation is, the new size of your view, and then change the frame of your AdWhirl view to the new location.
After looking a bit closer, however, it looks like you might need to make *adView a variable declared in your .h file so you can access it from the rotation methods.
Once you do that, you can set your new frame as you did in the viewDidLoad: method:
CGSize adSize = [adView actualAdSize];
CGRect newFrame = adView.frame;
newFrame.size = adSize;
newFrame.origin.x = (self.view.bounds.size.width - adSize.width)/ 2;
newFrame.origin.y = (self.view.bounds.size.height - adSize.height);
adView.frame = newFrame;
[UIView commitAnimations];
Ideally, you would move this code into its own method so you can just call it from wherever you want in your view controller code (e.g. viewDidLoad and the rotation function(s)).
Thanks for your help - it was close to a solution (only just got it working tonight - had mostly forgotten about doing it!). After I made you changes to my .h, I was trying to call [adWhirlView adWhirlDidReceiveAd:(AdWhirlView *)adView]. This kept returning errors, even though it was defined in the AdWhirlView class. As a fix, I added -(void) adWhirlDidReceiveAd:(AdWhirlView *)adView and then called each time the frame rotated [self adWhirlDidReceiveAd:(AdWhirlView *)adView];.
Thanks again - so glad it's finally working.
Sam
I have an NSView subclass that can be dragged around in its superview. I move the views by calling NSView's setFrameOrigin and setFrameRotation methods in my mouseDragged event handler. The views are both moved and rotated with each call.
I have multiple instances of these views contained by a single superview. The problem I'm having is that, as one view is dragged over another, it leaves artifacts behind on the view it's eclipsing. I recorded a short video of this in action. Unfortunately, due to the video compression the artifacts aren't very visible.
I strongly suspect that this is related to the simultaneous translation and rotation. Quartz Debug reveals that a rectangle of the occluding (or occluded) view is updated as another view is dragged across it (video here); somehow this rectangle is getting miscalculated by the drawing engine, so part of the view that should be redrawn isn't.
The kicker is I have no idea how to fix this. I can't find any way to manually specify the update rect in the docs, nor am I sure that's what needs to happen. Any ideas? Thanks!
You might also consider using CALayers instead of views. Unlike views, layers are intended to be stacked with their siblings.
For a possible least-effort solution, try making the views layer-backed; it may or may not solve this problem, but it's worth a try.
Views aren't really designed to be stacked in an interactive fashion. Can be done, but edge cases abound.
Generally, for this kind of thing you would use a Cell like infrastructure if you want to do in-view dragging (See the Sketch example) and you would use the drag-n-drop infrastructure if you want to drag between views or windows (or apps).
If you really want to drag a transformed view over the top, you'll need to invalidate a rectangle of the view underneath the view being dragged. The rectangle will need to be bigger by a few pixels than the total area (unrotated/untransformed) that is obscured by the view being dragged. The artifacts are, effectively, caused by rounding error; diagonal lines are just an estimate on a raster drawing system.
See the method:
- (void)setNeedsDisplayInRect:(NSRect)invalidRect;