Problem initializing an object with init in Objective-C - iphone

I have an object that i intalize it's propertis with call to an init function, it works fine,
when i tried to add another object and intalize it the first object didn't get the properites, how do i initalize it to more then one object with diffrent or the same properties?
- (void)viewDidLoad {
pic1 = [[peopleAccel alloc] init];
}
Class peopleAccel:
- (id) init
{
self = [super init];
if (self != nil) {
position = CGPointMake(100.0, 100.0);
velocity = CGPointMake(4.0, 4.0);
radius = 40.0;
bounce = -0.1f;
gravity = 0.5f;
dragging = NO;
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
acceleratedGravity = CGPointMake(0.0, gravity);
}
return self;
}

I see a problem with setting the delegate of sharedAccelerometer. Only one object can be a delegate of another object at a time. So you should create only one peopleAccel object.
EDIT:
If you need to send accelerometer events to more than one object, you can create a specific delegate object in charge of receiving accelerometer events and broadcasting them to your several peopleAccel objects via notifications. See this question for some hints: NSNotificationCenter vs delegation?

Create a proxy so multiple objects can receive accelerometer events.
Whether you should do this or use NSNotificationCenter is debatable and there are two camps, but personally I would use this approach. NSNotificationCenter has to check string names to recognise event type; this kind of approach could be ever so slightly faster especially with a bit more optimisation. A bit more typing but I would say also easier for someone else to follow.
Something like this...
/* PLUMBING */
/* in headers */
#protocol MyAccelSubDelegate
-(void)accelerometer:(UIAccelerometer*)accelerometer
didAccelerate:(UIAcceleration*)acceleration;
#end
#interface MyAccelSubDelegateProxy : NSObject <UIAccelerometerDelegate> {
NSMutableArray subDelegates;
}
-(id)init;
-dealloc;
-(void)addSubDelegate:(id<MyAccelSubDelegate>)subDelegate;
-(void)removeSubDelegate:(id<MyAccelSubDelegate>)subDelegate;
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration;
#end
/* in .m file */
#implementation MyAccelSubDelegateProxy
-(id)init { self = [super init];
if (self!=nil) subDelegates = [[NSMutableArray alloc] init]; return self; }
-dealloc { [subDelegates release]; }
-(void)addSubDelegate:(id<MyAccelSubDelegate>)subDelegate {
[subDelegates insertObject:subDelegate atIndex:subDelegates.count]; }
-(void)removeSubDelegate:(id<MyAccelSubDelegate>)subDelegate {
for (int c=0; c < subDelegates.count; c++) {
id<MyAccelSubDelegate> item = [subDelegates objectAtIndex:c];
if (item==subDelegate) { [subDelegates removeObjectAtIndex:c];
c--; continue; }
}
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration {
for (int c=0; c < subDelegates.count; c++)
[((id<MyAccelSubDelegate>)[subDelegates objectAtIndex:c])
accelerometer:accelerometer didAccelerate:acceleration];
}
#end
/* SOMEWHERE IN MAIN APPLICATION FLOW STARTUP */
accelProxy = [[MyAccelSubDelegateProxy alloc] init];
[UIAccelerometer sharedAcclerometer].delegate = accelProxy;
[UIAccelerometer sharedAcclerometer].updateInterval = 0.100; // for example
/* TO ADD A SUBDELEGATE */
[accelProxy addSubDelegate:obj];
/* TO REMOVE A SUBDELEGATE */
[accelProxy removeSubDelegate:obj];
/* SOMEWHERE IN MAIN APPLICATION SHUTDOWN */
[UIAccelerometer sharedAcclerometer].delegate = nil;
[accelProxy release];

Related

Quartz 2D MVC Drawing

all. I'm trying to follow a tutorial on making a ball bounce around the screen of an iPhone. The tutorial constructs the application in a MVC scheme. I'm having trouble wrapping my head around this concept when it comes to the drawRect method in the View implementation.
This is my Model header file:
#import <Foundation/Foundation.h>
#import "TestView.h"
#define BALL_SIZE 20.0
#define VIEW_WIDTH 320.0
#define VIEW_HEIGHT 460.0
#interface TestModel : NSObject
{
TestView* ball;
CGPoint ballVelocity;
CGFloat lastTime;
CGFloat timeDelta;
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp;
- (void) checkCollisionWithScreenEdges;
#property (readonly) TestView* ball;
#end
The tutorial instructs me the user to override the init method of NSObject. I've also included the methods for controlling the "animation" logic:
- (id) init {
self = [super init];
if (self) {
ball = [[TestView alloc] initWithFrame: CGRectMake(0.0, 0.0, BALL_SIZE, BALL_SIZE)];
// Set the initial velocity for the ball
ballVelocity = CGPointMake(200.0, -200.0);
// Initialize the last time
lastTime = 0.0;
}
return self;
}
- (void) checkCollisionWithScreenEdges {
// Left Edge
if (ball.frame.origin.x <= 0) {
ballVelocity.x = abs(ballVelocity.x);
}
// Right Edge
if (ball.frame.origin.x >= VIEW_WIDTH - BALL_SIZE) {
ballVelocity.x = -1 * abs(ballVelocity.x);
}
// Top Edge
if (ball.frame.origin.y <= 0) {
ballVelocity.y = abs(ballVelocity.y);
}
// Bottom Edge
if (ball.frame.origin.y >= VIEW_HEIGHT - BALL_SIZE) {
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp {
if (lastTime == 0.0) {
// initialize lastTime if first time through
lastTime = timestamp;
} else {
// Calculate time elapsed since last call
timeDelta = timestamp - lastTime;
// Update the lastTime
lastTime = timestamp;
[self checkCollisionWithScreenEdges];
// Calculate the new position of the ball
CGFloat x = ball.frame.origin.x + ballVelocity.x * timeDelta;
CGFloat y = ball.frame.origin.y + ballVelocity.y * timeDelta;
ball.frame = CGRectMake(x, y, BALL_SIZE, BALL_SIZE);
}
}
The View implementation file is the following:
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect) rect {
}
#end
Finally, my View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
gameModel = [[TestModel alloc] init];
[self.view addSubview:gameModel.ball];
// Set up the CADisplayLink for the animation
gameTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateDisplay:)];
// Add the display link to the current run loop
[gameTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) updateDisplay:(CADisplayLink *) sender {
[gameModel updateModelWithTime:sender.timestamp];
}
OK, so now that I've provided a look at the structure of the code (hopefully I've given enough) I can get to my question. So when I add anything to drawRect a new object is drawn and does not get "animated" by the model logic methods.
Right now I have a bouncing square. When I try to fill the square with an ellipse in drawRect, I get a new object, drawn how I want, that just sits at 0,0 while the bouncing square is still active.
I'm sure I'm missing something really big here, but I've been banging my head against the wall for hours and can't figure it out. Any help would be greatly appreciated!
A couple of things here:
1- Have you overriden the view class in SB to TestView?
2- Looking at your code, I do not see how your model is connected to your View through your controller. Recall from the MVC model, that the Controller is on top and talks down (pyramid style) to the model and the view and so, somewhere in your view controller:
a) you need to instantiate your model
b) get values from your model and pass them to your view variables - which would be called in drawrect
Last but not least, lookup setNeedsDisplay which is the way to call and refresh the view.
Hope this helps without spoiling the assignment.

run time catch method [duplicate]

I am developing an Objective-C application, and what I want to do, is something like the following:
+-----------------+ +---------------+
| Some Object | <---------- | Synchronize |
|(Not Thread Safe)| | Proxy |
+-----------------+ / +---------------+
/
/ Intercepts [someobject getCount]
/ #synchronize (someObject)
/
[someObject getCount] /
+----------------------+
| Some Calling Object |
+----------------------+
What I've asking is, how can I create an object in objective-c, that intercepts messages sent to another object, in order to perform code before the message is sent to that object.
Some things that I think will not work:
Categories (I need this to only happen for certain instances of a class)
Rewriting the object (I don't have access to the source of the object)
Method swizzling (once again, this need to only happen for certain instances of a class)
You would implement an NSProxy that forwards messages to your non-thread-safe object.
Here is a nice writeup of message forwarding in Objective-C, and here is Apple's documentation.
To handle thread safety, it depends on what you need. If your non-thread-safe object must run on a specific thread then you can use a NSRunLoop on said thread to serialize messages to that object.
Here is an example of using NSInvocation in conjunction with NSRunLoop. In that example they're using performSelector:withObject:afterDelay: but to use it with performSelector:onThread:withObject:waitUntilDone: would be very similar.
Otherwise, just use a single NSRecursiveLock in your proxy.
If you know exactly what instances should have the behavior you are trying to achieve you can go with method swizzling and call the base implementation if the instance is not the one you are looking for.
You can have a global shared object that lists the "interesting" instances and use it in the swizzling implementation whether you have to call the base one or your custom one.
So, I bit the bullet, and decided to make my own proxy class. To subclass, you simply override the 'forwardInvocation:' message, and you call any code you need there, before calling [super forwardInvocation:]. Please not this will NOT work with vardic methods, as NSInvocation doesn't work with vardic methods.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/objc.h>
#import <objc/message.h>
#interface RJProxy : NSObject {
#private
NSObject *target;
}
#property(readwrite, retain) NSObject *target;
-(NSObject *) getTarget;
#end
#implementation RJProxy
#synthesize target;
-(NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector
{
if (objc_getAssociatedObject(self, "isProxy"))
{
IMP NSObjectImp = [NSObject instanceMethodForSelector:#selector(methodSignatureForSelector:)];
NSMethodSignature *methodSignature = (NSMethodSignature *) NSObjectImp(self, #selector(methodSignatureForSelector:), aSelector);
if (methodSignature)
return methodSignature;
return [target methodSignatureForSelector:aSelector];
}
else
{
Class subClass = self->isa;
#try {
self->isa = objc_getAssociatedObject(self, "realSuperclass");
return [super methodSignatureForSelector:aSelector];
}
#finally {
self->isa = subClass;
}
}
}
-(void) forwardInvocation:(NSInvocation *)anInvocation
{
if (objc_getAssociatedObject(self, "isProxy"))
{
Class subClass = target->isa;
target->isa = objc_getAssociatedObject(self, "realSuperclass");
[anInvocation invokeWithTarget:target];
target->isa = subClass;
}
else
{
Class realSuperclass = objc_getAssociatedObject(self, "realSuperclass");
Class subclass = self->isa;
self->isa = realSuperclass;
if ([self respondsToSelector:[anInvocation selector]])
{
[anInvocation invokeWithTarget:self];
}
else
{
[self doesNotRecognizeSelector:[anInvocation selector]];
}
self->isa = subclass;
}
}
-(NSObject *) getTarget
{
if (objc_getAssociatedObject(self, "isProxy"))
{
return target;
}
return self;
}
#end
BOOL object_setProxy(NSObject *object, RJProxy *proxy);
BOOL object_setProxy(NSObject *object, RJProxy *proxy)
{
proxy.target = object;
Class objectClass = object_getClass(object);
Class objectSub = objc_allocateClassPair(objectClass, [[NSString stringWithFormat:#"%s_sub%i", class_getName(objectClass), objc_getAssociatedObject(objectClass, "subclassTimes")] UTF8String], 0);
objc_setAssociatedObject(objectClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(objectClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
objc_registerClassPair(objectSub);
Class proxyClass = object_getClass(proxy);
Class proxySub = objc_allocateClassPair(proxyClass, [[NSString stringWithFormat:#"%s_sub%i", class_getName(proxyClass), objc_getAssociatedObject(proxyClass, "subclassTimes")] UTF8String], 0);
objc_setAssociatedObject(proxyClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(proxyClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
objc_registerClassPair(proxySub);
object_setClass(object, proxySub);
object_setClass(proxy, proxySub);
objc_setAssociatedObject(object, "isProxy", (id) NO, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(proxy, "isProxy", (id) YES, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(object, "realSuperclass", objectClass, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(proxy, "realSuperclass", proxyClass, OBJC_ASSOCIATION_ASSIGN);
return NO;
}
#interface SynchronizeProxy : RJProxy
#end
#implementation SynchronizeProxy
-(void) forwardInvocation:(NSInvocation *)anInvocation {
#synchronized ([self getTarget])
{
[super forwardInvocation:anInvocation];
}
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSArray *arrayToSynchronize = [NSArray arrayWithObjects:#"This, is, a, test!", nil];
SynchronizeProxy *myProxy = [SynchronizeProxy new];
object_setProxy(arrayToSynchronize, myProxy);
// now all calls will be synchronized!
NSLog(#"Array at address 0x%X with count of %lu, and Objects %# ", (unsigned) arrayToSynchronize, [arrayToSynchronize count], arrayToSynchronize);
[myProxy release];
[arrayToSynchronize release];
}
return 0;
}

accessing instance variable in cocos2d scheduled method crashes

fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466
After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.

Misunderstanding on Cocos2D: Layers, static variables and Model View Controller. Can those coexist?

sorry if the question is too dull but I have been trying to understand as much as possible from Itterheim's book book's code example and can't figure out how this works.
In my "HelloWorldLayer.m" I create an instance of a class named "MusicLayer" that extends CCLayer The inspiration of this was that in the example named "ShootEmUp" (Chapter 8 of 1) there is an InputLayer. I thus thought that I would create a MusicLayer to deal with the various music files I have in my game and got some problems (will explain at the end of the post). I got most of the music related code code from RecipeCollection02, Ch6_FadingSoundsAndMusic of the Cocos2d Cookbook .
But first I would like to introduce some of my background thoughts that might have lead to this problem with the hope to have some clear reference on this.
I am used to a "model view controller" (MVC wikipedia link) approach as I come from a C++ and Java background and never coded a game before and it does seem to me that the gaming paradigms are a bit different from "the classic" Software Engineering University approach (don't take me wrong, I am sure that in many Universities they don't teach this stuff anymore :)).
The point is that it seems to me that the "classic" Software Engineering approach is lost in those examples (for instance in the ShootEmUp code the instance of InputLayer has a reference of the class GameScene -see below- and at the same time in InputLayer there is a reference to the static shared instance of GameScene -see below-).
//From GameScene
+(id) scene
{
CCScene* scene = [CCScene node];
GameScene* layer = [GameScene node];
[scene addChild:layer z:0 tag:GameSceneLayerTagGame];
InputLayer* inputLayer = [InputLayer node];
[scene addChild:inputLayer z:1 tag:GameSceneLayerTagInput];
return scene;
}
//From InputLayer
-(void) update:(ccTime)delta
{
totalTime += delta;
// Continuous fire
if (fireButton.active && totalTime > nextShotTime)
{
nextShotTime = totalTime + 0.5f;
GameScene* game = [GameScene sharedGameScene];
ShipEntity* ship = [game defaultShip];
//Code missing
}
}
I was told that static instances are things to avoid. Despite this I understand the benefit of having a GameScene staic instance but I still get confused on the biderectional reference (from GameScene to InputLayer and viceversa)..:
Is this common practice in Game programming?
Is there a way to avoid this approach?
I will now paste my code and I admit it, is probably rubbish and there will be probably some obvious error as I am trying to do a very simple thing and I do not manage.
So here we are. That's the MusicLayer class (it contains commented out code as I had previously tried to make it a shared class instance):
//
// MusicLayer.h
// ShootEmUp
//
// Copyright 2012 __MyCompanyName__. All rights reserved.
//
//Enumeration files should be the way those are referred from outside the class..
enum music_files {
PLAYER_STANDARD = 1,
};
enum sound_files{
A = 0,
B = 1,
C = 2,
ABC = 4,
AAC = 5,
ACA = 6,
//And so on and so forth..
};
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CDXPropertyModifierAction.h"
#interface MusicLayer : CCLayer {
SimpleAudioEngine *sae;
NSMutableDictionary *soundSources;
NSMutableDictionary *musicSources;
}
//In this way you can load once the common sounds on the sharedMusicLayer, have the benefit of keep tracks playing on scene switching as well as being able to load tracks and sounds before each level
//+(MusicLayer*) sharedMusicLayer;
/** API **/
//PLAY - sound undtested
-(void) playBackgroundMusic:(enum music_files) file;
-(void) playSoundFile:(enum sound_files) file;
-(void) loadPopLevel;
/** Private methods **/
//Utilities methods
-(NSString *) _NSStringFromMusicFiles: (enum music_files) file;
-(NSString *) _NSStringFromSoundFiles: (enum sound_files) file;
//LOAD - Meant to be private methods
-(CDLongAudioSource*) _loadMusic:(NSString*)fn;
-(CDSoundSource*) _loadSoundEffect:(NSString*)fn;
//FADE - sound undtested
-(void) _fadeOutPlayingMusic;
-(void) _fadeInMusicFile:(NSString*)fn;
-(void) _playSoundFile:(NSString*)fn;
#end
And here we are with the .m file:
#import "MusicLayer.h"
#implementation MusicLayer
/**
static MusicLayer* instanceOfMusicLayer;
+(MusicLayer*) sharedMusicLayer
{
NSAssert(instanceOfMusicLayer != nil, #"MusicLayer instance not yet initialized!");
return instanceOfMusicLayer;
}**/
-(id) init
{
CCLOG(#"In Init");
if ((self = [super init]))
{
CCLOG(#"Inside Init");
//instanceOfMusicLayer = self;
//Initialize the audio engine
sae = [SimpleAudioEngine sharedEngine];
//Background music is stopped on resign and resumed on become active
[[CDAudioManager sharedManager] setResignBehavior:kAMRBStopPlay autoHandle:YES];
//Initialize source container
soundSources = [[NSMutableDictionary alloc] init];
musicSources = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void) loadPopLevel{
CCLOG(#"In loadPopLevel");
[self _loadSoundEffect:[self _NSStringFromSoundFiles:A]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:B]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:C]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:ACA]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:ABC]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:AAC]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
}
/** UTILITIES METHODS **/
//This function is key to define which files we are going to load..
-(NSString *) _NSStringFromMusicFiles: (enum music_files) file{
CCLOG(#"In _NSStringFromMusicFiles");
switch (file) {
case PLAYER_STANDARD:
return #"a.mp3";
default:
NSAssert(0 == 1, #"Invalid argument");
break;
}
}
-(NSString *) _NSStringFromSoundFiles: (enum sound_files) file{
CCLOG(#"In _NSStringFromSoundFiles");
switch (file) {
case A:
return #"shot.caf";
case B:
return #"shot.caf";
case C:
return #"shot.caf";
case AAC:
return #"Wow.caf";
case ABC:
return #"Wow.caf";
case ACA:
return #"Wow.caf";
default:
NSAssert(0 == 1, #"Invalid argument");
break;
}
}
/** PLAY METHODS **/
-(void) _playSoundFile:(NSString*)fn {
CCLOG(#"In _playSoundFile");
//Get sound
CDSoundSource *sound = [soundSources objectForKey:fn];
sound.looping = YES;
//Play sound
if(sound.isPlaying){
[sound stop];
}else{
[sound play];
}
}
-(void) _fadeInMusicFile:(NSString*)fn {
CCLOG(#"In _fadeInMusicFile");
//Stop music if its playing and return
CDLongAudioSource *source = [musicSources objectForKey:fn];
if(source.isPlaying){
[source stop];
return;
}
//Set volume to zero and play
source.volume = 0.0f;
[source play];
//Create fader
CDLongAudioSourceFader* fader = [[CDLongAudioSourceFader alloc] init:source interpolationType:kIT_Linear startVal:source.volume endVal:1.0f];
[fader setStopTargetWhenComplete:NO];
//Create a property modifier action to wrap the fader
CDXPropertyModifierAction* fadeAction = [CDXPropertyModifierAction actionWithDuration:1.5f modifier:fader];
[fader release];//Action will retain
[[CCActionManager sharedManager] addAction:[CCSequence actions:fadeAction, nil] target:source paused:NO];
}
-(void) _fadeOutPlayingMusic {
CCLOG(#"In _fadeOutPlayingMusic");
for(id m in musicSources){
//Release source
CDLongAudioSource *source = [musicSources objectForKey:m];
if(source.isPlaying){
//Create fader
CDLongAudioSourceFader* fader = [[CDLongAudioSourceFader alloc] init:source interpolationType:kIT_Linear startVal:source.volume endVal:0.0f];
[fader setStopTargetWhenComplete:NO];
//Create a property modifier action to wrap the fader
CDXPropertyModifierAction* fadeAction = [CDXPropertyModifierAction actionWithDuration:3.0f modifier:fader];
[fader release];//Action will retain
CCCallFuncN* stopAction = [CCCallFuncN actionWithTarget:source selector:#selector(stop)];
[[CCActionManager sharedManager] addAction:[CCSequence actions:fadeAction, stopAction, nil] target:source paused:NO];
}
}
}
/** LOADING METHODS **/
-(CDLongAudioSource*) _loadMusic:(NSString*)fn {
CCLOG(#"In _loadMusic" );
//Init source
CDLongAudioSource *source = [[CDLongAudioSource alloc] init];
source.backgroundMusic = NO;
[source load:fn];
//Add sound to container
[musicSources setObject:source forKey:fn];
return source;
}
-(CDSoundSource*) _loadSoundEffect:(NSString*)fn {
CCLOG(#"In _loadSoundEffect" );
//Pre-load sound
[sae preloadEffect:fn];
//Init sound
CDSoundSource *sound = [[sae soundSourceForFile:fn] retain];
//Add sound to container
[soundSources setObject:sound forKey:fn];
return sound;
}
/** Public methods **/
//Play music callback
-(void) playBackgroundMusicNumber:(enum music_files) file {
CCLOG(#"In playBackgroundMusic");
switch (file) {
case PLAYER_STANDARD:
[self _fadeOutPlayingMusic];
[self _fadeInMusicFile: [self _NSStringFromMusicFiles:PLAYER_STANDARD]];
break;
default:
break;
}
}
-(void) playSoundFile:(enum sound_files) file {
CCLOG(#"In playSoundFile");
switch (file) {
case A:
[self _playSoundFile:[self _NSStringFromSoundFiles:A]];
break;
case B:
[self _playSoundFile:[self _NSStringFromSoundFiles:B]];
break;
case C:
[self _playSoundFile:[self _NSStringFromSoundFiles:C]];
break;
case AAC:
[self _playSoundFile:[self _NSStringFromSoundFiles:AAC]];
break;
case ABC:
[self _playSoundFile:[self _NSStringFromSoundFiles:ABC]];
break;
case ACA:
[self _playSoundFile:[self _NSStringFromSoundFiles:ACA]];
break;
default:
break;
}
}
/** Dealloc ! **/
-(void) dealloc {
[sae stopBackgroundMusic];
for(id s in soundSources){
//Release source
CDSoundSource *source = [soundSources objectForKey:s];
if(source.isPlaying){ [source stop]; }
[source release];
}
[soundSources release];
for(id m in musicSources){
//Release source
CDLongAudioSource *source = [musicSources objectForKey:m];
if(source.isPlaying){ [source stop]; }
[source release];
}
[musicSources release];
//End engine
[SimpleAudioEngine end];
sae = nil;
//instanceOfMusicLayer = nil;
[super dealloc];
}
#end
Now, I created a new Ccoos2d Helloworld template and added the following to the .m class to test the code:
#import "cocos2d.h"
#import "MusicLayer.h"
enum tags {
MUSICLAYERTAG = 99,
};
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer
{
}
+(CCScene *) scene;
#end
//
// HelloWorldLayer.m
// MusicFadingTest
//
// Import the interfaces
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(CCScene *) scene
{
CCLOG(#"In helloworld scene");
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
MusicLayer *musicLayer = [MusicLayer node];
[musicLayer loadPopLevel];
[scene addChild:musicLayer z:-1 tag:MUSICLAYERTAG];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
//Add menu items
[CCMenuItemFont setFontSize:20];
CCMenuItemFont *music0Item = [CCMenuItemFont itemFromString:#"Song A" target:self selector:#selector(play:)];
music0Item.tag = 0;
CCMenuItemFont *music1Item = [CCMenuItemFont itemFromString:#"Song B" target:self selector:#selector(play:)];
music1Item.tag = 1;
CCMenuItemFont *music2Item = [CCMenuItemFont itemFromString:#"Song C" target:self selector:#selector(play:)];
music2Item.tag = 2;
CCMenuItemFont *music3Item = [CCMenuItemFont itemFromString:#"Song D" target:self selector:#selector(play:)];
music3Item.tag = 3;
CCMenuItemFont *music4Item = [CCMenuItemFont itemFromString:#"Sound A" target:self selector:#selector(play:)];
music2Item.tag = 4;
CCMenuItemFont *music5Item = [CCMenuItemFont itemFromString:#"Sound B" target:self selector:#selector(play:)];
music3Item.tag = 5;
//Create our menus
CCMenu *menu0 = [CCMenu menuWithItems:music0Item, music1Item, music2Item, music3Item, music4Item, music5Item, nil];
[menu0 alignItemsInColumns: [NSNumber numberWithUnsignedInt:6], nil];
menu0.position = ccp(240,240);
[self addChild:menu0];
}
return self;
}
//Play music callback
-(void) play:(id)sender {
CCNode* node = [self getChildByTag:MUSICLAYERTAG];
NSAssert([node isKindOfClass:[MusicLayer class]], #"not a MusicLayer");
MusicLayer *musicLayer = (MusicLayer*)node;
CCMenuItem *item = (CCMenuItem*)sender;
switch (item.tag ) {
case 1:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 2:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 3:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 4:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 5:
[musicLayer playSoundFile:A];
break;
case 6:
[musicLayer playSoundFile:B];
break;
default:
break;
}
}
- (void) dealloc
{
[super dealloc];
}
#end
But here is the error message that I get when I the simulator I try to press any button and hence trigger the play method in HelloWorld.m
2012-04-23 10:39:18.493 MusicFadingTest[1474:10a03] In _loadMusic
2012-04-23 10:39:18.510 MusicFadingTest[1474:10a03] cocos2d: Frame interval: 1
2012-04-23 10:39:19.405 MusicFadingTest[1474:10a03] *** Assertion failure in -[HelloWorldLayer play:], /Users/user/Desktop/MusicFadingTest/MusicFadingTest/HelloWorldLayer.m:76
2012-04-23 10:39:19.406 MusicFadingTest[1474:10a03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'not a MusicLayer'
*** First throw call stack:
(0x17f6022 0x1987cd6 0x179ea48 0x11c62cb 0xc264a 0x175c4ed 0x175c407 0x3a3b5 0x3ad73 0x37772 0x17f7e99 0x92821 0x9336f 0x95221 0x8573c0 0x8575e6 0x83ddc4 0x831634 0x27b1ef5 0x17ca195 0x172eff2 0x172d8da 0x172cd84 0x172cc9b 0x27b07d8 0x27b088a 0x82f626 0xc107f 0x2955)
The code above kind of mimics what happenes in the ShootEmUp example but I am missing something. As I cannot get the child by tag..
I asked this question as despite the answer will be probably trivial I hope to get some clarification on the general NON-ModelViewController approach in game programming and the usage of static variables.
I imagine using my MusicLayer in the MainMenu class and in the various layers implementing my levels. I would preload various music files according to the level the player is playing and keep the files that are not level specific preloaded (obviously taking care on the maximum number of sound files supported by the AudioEngine).
The other approach would have been to have a different instance of MusicLayer for each level and initializing them with different music files. The disadvantage of this approach is that the sharedAudioEngine is only one and when you want to have files keep playing between one scene and the other there is the risk of not having a full control on the track numbers used in the sharedAudioEngine (I recall should be 32 caf files for sound effect and few background tracks).
I thus understand why static instances are beneficial in game programming, but still, would love to hear what you think about the biderectional reference in 1 ShootEmUp code.
Also, would like to clarify that I encourage buying 1 as it has been a good starting point.
Thank you very much!
Oh, how much code and letters... First of all, for your question about "static instance". This is not static instance. This is static constructor. So, you can use smth like
CCScene* myScene = [GameScene scene];
instead of
CCScene* myScene = [[GameScene alloc] init];
// doing smth
[myScene release];
So, you will just create an autoreleased instance of your node (in that case, of your scene).
About the music question. You have add your music layer to the scene, but self in your play method will be HelloWorldLayer instance. So if you wanna get your music layer, you can try smth like this
MusicLayer* musicLayer = [[self parent] getChildByTag:MUSICLAYERTAG];

NSMutableArray and batchNode problems

I'm making a little game, here is some example code of whats going on:
-(id) init
{
self.arrowProjectileArray = [[[NSMutableArray alloc] init] autorelease];
self.batchNode = [CCSpriteBatchNode batchNodeWithTexture:[[CCTextureCache sharedTextureCache] addImage:#"arrow.png"]];
[self addChild:_batchNode z:2];
for (CCSprite *projectile in _arrowProjectileArray) {
[_batchNode removeChild:projectile cleanup:YES];
}
[_arrowProjectileArray removeAllObjects];
self.nextProjectile = nil;
}
}
-(void) callEveryFrame:(ccTime)dt{
for (int i = 0; i < [_arrowProjectileArray count];i++) {
CCSprite *cursprite = [_arrowProjectileArray objectAtIndex:i];
if (cursprite.tag == 1) {
float x = theSpot.x+10;
float y = theSpot.y+10;
cursprite.position = ccp(x, y);
}
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_batchNode addChild:_nextProjectile z:1 tag:1];
[_arrowProjectileArray addObject: _nextProjectile];
[self spriteMoveFinished];
}
-(void) dealloc
{
self.arrowProjectileArray = nil;
self.nextProjectile = nil;
[super dealloc];
}
The only code that I included was code that is relevant to the arrow's projection.
The arrow shoots fine, the problem is every time I shoot the stupid thing, I think it shoots a new arrow, but puts multiple arrows onto of that 1 arrow and makes it look like a fat ugly arrow pixel thing. What am I doing wrong? I'm not too familiar with NSMutableArray, but I'm currently stuck.
In init method, you create a new NSMutableArray instance and assign it to self.arrowProjectileArray, then you traverse the arrowProjectileArray in the following lines using a for loop. If addChild: method does not add anything to arrowProjectileArray, then your code has a logic mistake, because what you do by traversing arrowProjectileArray is traversing an empty array, which means you do nothing in that code.
You should double-check what you intend to do and what your code is doing actually.
I solved my own problem by doing a little bit of research, I also got rid of the batch node.