Cocos2d on iPhone: Using Sprites Defined in Separate Class Files - iphone-sdk-3.0

I'm trying to draw two sprites on the same screen. I have defined the two sprite objects in two separate class files.
If I comment out two lines (see "item 1" comment below) then I get a display as [a backgroundimage[background2.jpg] with a sprite[grossini.png] on the left side.
If I uncomment the two lines I do not get the background image and sprite of (gameScreen.m). I get only the sprite [grossinis_sister1.png] defined in (enemy.m).
But what I need is a [backgroundimage[background2.jpg]], sprite[grossini.png] and sprite [grossinis_sister1.png] in one screen.
This is implementation file of my first class:
#import "gameScreen.h"
#import "enemy.h"
#implementation gameScreen
-(id)init
{
if((self = [super init]))
{
CCSprite *backGroundImage = [CCSprite spriteWithFile:#"background2.jpg"];
backGroundImage.anchorPoint = ccp(0,0);
CCParallaxNode *voidNode = [CCParallaxNode node];
[voidNode addChild:backGroundImage z:-1 parallaxRatio:ccp(0.0f,0.0f) positionOffset:CGPointZero];
[self addChild:voidNode];
CGSize windowSize = [[CCDirector sharedDirector] winSize];
CCSprite *player = [CCSprite spriteWithFile:#"grossini.png"];
player.position = ccp(player.contentSize.width/2, windowSize.height/2);
[self addChild:player z:0];
//eSprite = [[enemy alloc]init]; //<-- see item 1
//[self addChild:eSprite];
}
return self;
}
This is my implementation file of my second class:
#import "enemy.h"
#import "gameScreen.h"
#implementation enemy
-(id)init
{
if ((self = [super init]))
{
CGSize windowSize = [[CCDirector sharedDirector] winSize];
CCSprite *enemySprite = [CCSprite spriteWithFile:#"grossinis_sister1.png" ];
enemySprite.position = ccp(windowSize.width/2, windowSize.height/2);
[self addChild:enemySprite];
}
return self;
}
#end

The high level understanding you need is this. A screen contains 1 or many layers a layer contains sprites.
So create a screen and then add a layer to it and add the sprites to the layer you created. Of course one can have many screens and a screen can consists of many layers. But in a simple demo game create 1 screen, 1 layer, and add sprites to that layer.
See this link for more detail http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:basic_concepts.

I am unfamiliar with Cocos2d but the normal pattern is to have a object that would own both gamescreen and enemy sprites and manage both.

Related

IOS 7 nested SKScenes with Physik Simulation

I would like to use simulated physics with nodes in nested SKScenes in my Spritekit Game for iOS7.
But it doesnt work like it should.
Adding the physicsbody to the object from the child scene after adding the child scene to the main scene dont throw an error but it doesnt seem to work correctly.
The phsics from the child scene doesnt work.
Is there a better way to separate the static world from the dynamic objects?
For example:
I have the first main Scene:
#interface MyScene : SKScene
// ...
#end
#implementation MyScene
-(id)initWithSize:(CGSize)size {
// ... setup blabla
// first object
SKSpriteNode *obj1 = [SKSpriteNode spriteNodeWithImageNamed:#"obj1"];
obj1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:obj1.size.height / 2];
// ... obj1.physicsBody setup with collisionBitMask
[self addChild:obj1];
// New SKScene with second Object
World *world = [[World alloc] initWithSize:size];
[self addChild:obj1];
[world addObjects];
}
#end
and the World SKScene Class:
#interface World : SKScene
// ...
#end
#implementation World
-(void)addObject{
// second object
SKSpriteNode *obj2 = [SKSpriteNode spriteNodeWithImageNamed:#"obj2"];
obj2.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:obj2.size.height / 2];
// ... obj2.physicsBody setup with collisionBitMask and dynamic = no
[self addChild:obj2];
}
#end
You can only have one scene running/active/presented at a time.
So the basic premise of having two scenes is futile. That physics isn't working (as expected) in this scenario is just a symptom.
While you can create scene objects and add them as child to another scene, this will not make them work like a scene but rather they're treated more like a regular SKNode. You should also notice that you won't receive contact callbacks in the sub-scene, nor will it receive update, didEvaluateActions and didSimulatePhysics messages.

scheduleOnce not working on my CCLayer class, when set in onEnter

I have a CCLayer class that is a child of a base class I created that is, itself, a child of CCLayer.
After creating this type of class hierarchy, I can not get the onEnter method of my child class to successfully schedule a method call. How can I fix this?
I am trying to get started on a gaming project but I've hit a bit of a brick wall. This seems to be a common problem, however, I can't find a scenario similar to mine and, thus, can't derive a solution.
I have a child CCLayer class that I've created, and it is essentially the exact same thing as the IntroLayer in the Cocos2D, starter iPhone template. In fact, my class is the exact same class with one major exception-- I created a base class that I derived from CCLayer, and that is used as my parent of my IntroLayer.
My problem is that in the onEnter method of my child layer, I see that my transition method is being set in my scheduleOnce assignment. The problem, though, is that my transition method is never being called?
I'm not sure what I am doing incorrectly. The code posted below is my .m file for a my IntroLayer.
IntroLayer.m
#implementation IntroLayer //Child of "MyBaseCCLayer"
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
IntroLayer *layer = [IntroLayer node];
[scene addChild: layer];
return scene;
}
-(void) onEnter
{
//This is calling an overridden onEnter of my base CCLayer.
// See snippet below.
[super onEnter];
CGSize size = [[CCDirector sharedDirector] winSize];
CCSprite *background;
background = [CCSprite spriteWithFile:#"Default.png"];
[background setPosition:ccp(size.width/2, size.height/2)];
[background setZOrder: Z_BACKGROUND];
[self addChild: background];
//In one second transition to the new scene
// I can put a break point here, and this being set.
// It never gets called, though.
[self scheduleOnce:#selector(makeTransition:) delay:1.0];
}
-(void) makeTransition:(ccTime)delay
{
//Here is where I would transition to my menu.
// This is a fresh project, so it's going to a test screen.
// The problem is this method never fires.
[[CCDirector sharedDirector]
replaceScene:[CCTransitionFade
transitionWithDuration:0.5
scene:[TestLayer scene]
withColor:ccBLACK]];
}
-(void) dealloc
{
[super dealloc];
}
#end
MyBaseCCLayer.m
//^boilerplate snip^
-(void) onEnter
{
//Setup overlay
CGSize size = [[CCDirector sharedDirector] winSize];
ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
//overlay is a class variable, CCSprite object.
overlay = [CCSprite spriteWithFile:#"tile_overlay.png" rect:CGRectMake(0, 0, size.width, size.height)];
[overlay retain];
[overlay setPosition: ccp(size.width/2, size.height/2)];
[overlay.texture setTexParameters:&tp];
//Always on top!
[overlay setZOrder:Z_OVERLAY];
}
-(void) dealloc
{
[overlay release];
[super dealloc];
}
As you can see, there is nothing ground breaking going on here. I created this base class because I want to apply the overlay sprite to ever CCLayer in my application. This is why it made sense to create my own base, CCLayer. However, now that I've created my own base class, my IntroLayer won't call my scheduled method that I'm setting in onEnter. How can I get this to work?
Your custom class MyBaseCCLayer is overriding the onEnter method, but you omitted the call to [super onEnter]. It's easy to forget sometimes, but calling [super onEnter] is critical to the custom class. Here is the description of the method from the Cocos2D documentation:
Event that is called every time the CCNode enters the 'stage'. If the
CCNode enters the 'stage' with a transition, this event is called when
the transition starts. During onEnter you can't access a sibling
node. If you override onEnter, you shall call [super onEnter].
If you take a look inside CCNode.m, you will see what happens inside onEnter:
-(void) onEnter
{
[children_ makeObjectsPerformSelector:#selector(onEnter)];
[self resumeSchedulerAndActions];
isRunning_ = YES;
}
So you can see that if you override the onEnter method in your custom class, and you don't call [super onEnter], you miss out on this functionality. Essentially your custom node will remain in a 'paused' state.

how do i clear a draw uiview?

I am currently trying to refresh or clear my UIView draw but a lot of methods aren't working. I tried:
self.Draw.clearsContextBeforeDrawing
[self.Draw setNeedsDisplay];
self.Draw.removeFromSuperview;
My code:
-(void)drawPoint:(UITouch *)touch
{
PointLocation *currentLoc = [[PointLocation alloc] init];
currentLoc.location = [touch locationInView:self];
self.previousPoint = self.point;
self.point = currentLoc;
[self drawToBuffer];
[self setNeedsDisplay];
}
Is there any way to clear my uidraw? Or at least reload it?
There's a few basic problems here.
How is Draw declared? Follow cocoa naming conventions. Only Class names should be capitalized. Instances of a class should start with a lowercase letter.
I think what you want is:
#interface Draw: UIView
#end
#implementation Draw
- (void) drawRect: (CGRect) r {
// here you will draw whatever you want in your view.
}
#end
Draw* draw;
in your drawPoint method:
[draw setNeedsDisplay];
For more details see The View Drawing Cycle:
http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW136
For bonus points, name your view something more descriptive than Draw. It is a view of something, a window containing some content. It is not a verb.

Accessing CCLayer headache!

I'm having abit of trouble accessing my layers and its driving me insane. Basically, my layer-scene hierachy is as follows:
Compiler.m - CCLayer - holds the +(CCScene) method and loads all the other CCLayers.
Construct.m - CCLayer - holds the box2d engine and its components
Background.m - CCLayer - holds the Background.
Hud.m - CCLayer - holds the HUD.
within the Compiler implementation class, I add the Scene and all the relevant nodes to the Compiler CCLayer:
#implementation Compiler
+(CCScene *) scene{
Compiler *compiler = [CompileLayer node];
CCScene *scene = [CCScene node];
[scene addChild: compiler];
//Add A Background Layer.
Background *layerBackground = [Background node];
layerBackground.position = CGPointMake(100,100);
[compiler addChild: layerBackground z: -1 tag:kBackground];
//Add The Construct.
Construct *construct = [Construct node];
[compiler addChild: construct z: 1];
//Add A Foreground Layer Z: 2
//After background is working.
//Add the HUD
HudLayer *hud = [Hud node];
[compiler addChild: hud z:3];
}
This all works fine, my layers are added to the compiler and the compiler's scene is accessed by the delegate as predicted.
My problem is that im trying to access my Background CCLayers - CCsprite *background, inside the Construct layer so that I can move it according to the position of my Construct game hero.
I've tried many different methods, but I've currently decided to use a class method rather than an instance method to define CCSprite *background so that I can access it in my Construct Layer.
I've also tried accessing with #properties and initialising an instance variable of the class.
Here is my Background CCLayer:
#implementation Background
-(id) init
{
self = [super init];
if (self != nil)
{
CCSprite *temp = [Background bk];
[self addChild:temp z:0 tag:kBackGround];
}
return self;
}
+(CCSprite *)bk{
//load the image files
CCSprite *background = [CCSprite spriteWithFile:#"background.jpg"];
//get the current screen dimensions
//CGSize size = [[CCDirector sharedDirector] winSize];
background.anchorPoint = CGPointMake(0,0);
background.position = ccp(0,0);
return background;
}
#end
This works, it loads the image to the background layer.
Then finally, I try to access the background image from the Construct Layer.
#interface Construct : CCLayer{
CCSprite *tempBack;
}
#end
#implementation Construct
-(id) init{
tempBack = [Background bk]; //The background equals an instance variable
}
-(void) tick:(ccTime)dt {
tempBack.position.x ++; // To do Something here.
tempBack.opacity = 0.5; // Some more stuff here.
}
#end
This doesnt work, I receive a 'nil' pointer in some respects, tempBack doesnt access the background properly, or at all.
How can I access and modify the Background CCLayers Class Variable +(CCSprite) bk??
It probably doesn't work because your tempBack iVar is autoreleased in compiler layer and you don't retain it. Here's how your init method should look like:
-(id) init{
tempBack = [[Background bk] retain]; //The background equals an instance variable
}
Also - don't forget to release it in dealloc. And I am not sure if it will work either since you'll have different background sprite returned by bk class method (try printing temp and tempBack variable addresses and you'll see).
Update for notifications
In background layer, create the sprite and add it to the layer. Then afterwards add this snippet of code to subscribe to notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateBackground:) name:#"PlayerChangedPosition" object:nil];
Then in construct layer's update method (or any other scheduled method) send this notification with new player coordinates:
[[NSNotificationCenter defaultCenter] postNotificationName:#"PlayerChangedPosition" object:player];
Now in background's layer, implement the updateBackground:(NSNotification *)aNotification method:
- (void)updateBackground:(NSNotification *)aNotification {
// object message returns player object passed when posting notification
CGPoint newCoords = [[aNotification object] position];
// adjust background position according to player position
}

Displaying an NSString on a Custom View

I have an interface that has an NSTextField, NSButton, and an NSView. When I type something in the NSTextfield and press the button, I want the text to be drawn in the NSView. So far I have everything connected and working, except for the view.
How can I connect the text and the view so that every time I press the button, the text is drawn to the view?
How can I connect the text and the view so that every time I press the button, the text is drawn to the view?
Views do their own drawing.
You need to give the view the string to draw, and then set the view as needing display. You'll do these in the action method that you wire the button up to.
First, your custom view class needs to have a property for the value (string, in this case) that it's going to display. From your action method, which should generally be on a controller object, send the view object a setFoo: message (assuming you named the property foo). That takes care of job one: The view now has the value to display.
Job two is even easier: Send the view a setNeedsDisplay: message, with the value YES.
That's it. The action method is two lines.
Of course, since views draw themselves, you also need your custom view to actually draw, so you need to implement the drawRect: method in that class. It, too, will be short; all you need to do is tell the string to draw itself.
Bindings
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/CocoaBindings/Concepts/WhatAreBindings.html#//apple_ref/doc/uid/20002372-CJBEJBHH
For simplicity's sake I didn't mention this before, but the app also has a speech element to speak the string. This aspect of the program works fine, so just ignore any messages involving the SpeakAndDraw class (it's actually misnamed and only includes a speech method, nothing about drawing).
View.m
#import "View.h"
#implementation View
#synthesize stringToDraw;
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setAttributes];
stringToDraw = #"Hola";
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
NSRect bounds = [self bounds];
[self drawStringInRect:bounds];
}
- (void)setAttributes
{
attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:[NSFont fontWithName:#"Helvetica"
size:75]
forKey:NSFontAttributeName];
[attributes setObject:[NSColor blackColor]
forKey:NSForegroundColorAttributeName];
}
- (void)drawStringInRect:(NSRect)rect
{
NSSize strSize = [stringToDraw sizeWithAttributes:attributes];
NSPoint strOrigin;
strOrigin.x = rect.origin.x + (rect.size.width - strSize.width)/2;
strOrigin.y = rect.origin.y + (rect.size.height - strSize.height)/2;
[stringToDraw drawAtPoint:strOrigin withAttributes:attributes];
}
#end
SpeakerController.m
#import "SpeakerController.h"
#implementation SpeakerController
- (id)init
{
speakAndDraw = [[SpeakAndDraw alloc] init];
view = [[View alloc] init];
[mainWindow setContentView:mainContentView];
[mainContentView addSubview:view];
return self;
}
- (IBAction)speakText:(id)sender
{
[speakAndDraw setStringToSay:[text stringValue]];
[speakAndDraw speak];
[view setStringToDraw:[text stringValue]];
[view setNeedsDisplay:YES];
NSLog(#"%#", view.stringToDraw);
NSLog(#"%#", [view window]);
}
#end