Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

TwinofSian

macrumors member
Original poster
Oct 23, 2011
53
0
I have been trying to port over a bullet engine which features a Bullet and BulletCache class into another project. My main difficulty is that the Bullet/BulletCache methods are defined with the assumption of a GameScene singleton. This is a bad practice and the code I am trying to port it to does not have a singleton GameScene class.

In fact, the only singelton this code has is for the Sound engine!


Code:
-(void) checkForBulletCollisions
{
	EnemyEntity* enemy;
	CCARRAY_FOREACH([batch children], enemy)
	{
		if (enemy.visible)
		{
			BulletCache* bulletCache = [[GameScene sharedGameScene] bulletCache];
			CGRect bbox = [enemy boundingBox];
			if ([bulletCache isPlayerBulletCollidingWithRect:bbox])
			{
				// This enemy got hit ...
				[enemy gotHit];
			}
		}
	}
}

Game classes:

Code:
@interface GameScene : CCScene
	+(void) newGame:(NSString*)levelFile;
	-(void) reloadGame:(NSString*)levelFile;
@end

@interface GameLayer : CCLayer
	{
		CGPoint cameraOffset,cameraMin,cameraMax;
	}
	-(void) moveCamera:(CGPoint)pos;
	-(CGPoint) getCameraPosition;
@end


the code calls the GameScene because in the original code, that is where the BulletCache is, but in my code, BulletCache is a subclass of LevelObject (which is a subclass of CCNode) and so my accessors are in the Level Object.

However, LevelObject is not a singelton; I need some tips on how to cleanly port this code over. Thanks!
 
There are various ways to do this. One would be to rewrite this checkForBulletCollisions method so it takes as parameters the various state values (list of enemies, bullet cache, whatever else) does its calculation and returns an array of enemies that gotHit. Then this method could live anywhere or be a simple function rather than a method.

Another would be to store as a property the owner of the bulletCache or a reference to the bulletCache itself. Then in this method just reference the bulletCache. For something like this you would typically set this property either in an init method or shortly after the object is created.

BTW, I would move the "bulletCache =" line outside of the loop in this method.
 
Hey PDev, thanks for the advice, definitely appreciate it. Being new to coding, I was caught up by your phrasing:

store as a property the owner of the bulletCache or a reference to the bulletCache itself. Then in this method just reference the bulletCache. For something like this you would typically set this property either in an init method or shortly after the object is created.

To that end, I created added the BulletCache class as a property in the Character class (subclass of LevelObject)

Code:
Character.h
 @property BulletCache,CGFloat life,maxLife,strength,dexterity;

Code:
Character.m
@synthesize BulletCache,life,maxLife,strength,dexterity;


Code:
-(void) checkForBulletCollisions
	{
		BulletCache* bulletCache;
		Character* character;
		CCARRAY_FOREACH([batch children], 
                                                        character)
		{
			if (character.visible)
			{
			CGRect bbox = [enemy boundingBox];
			if ([bulletCache 
                             isPlayerBulletCollidingWithRect:bbox])
				{
				// This character got hit ...
					[character gotHit:(id)sender data:
                                                        (void*)data];
				}
			}
		}
	
	}

I am still at loss about the init method you were describing. In which class's init method do I create a BulletCache object?
 
Last edited:
Well, I don't know where you're creating the BulletCache object. But there is some controller object that is doing the hit-testing and which needs access to the BulletCache object. So when you create this controller object either set its BulletCache property or pass the BulletCache object into its init method. Something like

Code:
MyController* controller = [[MyController alloc] init];
controller.bulletCache = theBulletCache;
or

Code:
MyController* controller = [[MyController alloc] initWithBulletCache:theBulletCache];

These are effectively the same. Do whichever makes sense to you.

Then in the checkForBulletCollisions method use self.bulletCache.
 
You've really made me think about how to tackle this, which is awesome, that really helps me.

However, I am truly stumped about this MyController class being instanced in the BulletCache.

There are two bullet-related classes: The Bullet Class, and the BulletCache class.

Code:
Bullet.h

Code:
@interface Bullet : LevelObject
{
	CGPoint velocity;
	bool isPlayerBullet;
}

@property (readwrite, nonatomic) CGPoint velocity;
@property (readwrite, nonatomic) bool isPlayerBullet;

+(id) bullet;

-(id)initWithBulletImage

-(void) shootBulletAt:(CGPoint)startPosition velocity:
       (CGPoint)vel frameName:(NSString*)frameName 
                                  isPlayerBullet:(bool)playerBullet;

-(void) update:(ccTime)delta;


@end


Code:
BulletCache.h

Code:
@interface BulletCache : LevelObject

{
	CCSpriteSheet* batch;
	int nextInactiveBullet;
}

-(id) init;

-(void) shootBulletFrom:(CGPoint)startPosition 
                               velocity:(CGPoint)velocity 
	            frameName:(NSString*)frameName 
                          isPlayerBullet:(bool)isPlayerBullet;

-(bool) isPlayerBulletCollidingWithRect:(CGRect)rect;


@end


I am genuinely curious about this MyController however, it does not appear to be mentioned anywhere in the project I was porting the code from.
 
Last edited:
I have ported over a Bullet and BulletCache class. I was able to get the game to compile on the simulator with 1 warning even with the Bullet and BulletCache classes implemented.


The Bullet is a subclass of CCZSprite, while BulletCache is a subclass of CCNode.

HOWEVER:

In the Player (a subclass of Character) class I am overriding a Character method:


Code:
-(void) attack:(Character*)attackee
	{
		// get direction for animation
		[self pointToward:attackee.position];

	// create attack sequence (animation + gotHit callback)
	CCAction* action = [CCSequence actions:
[CCAnimate actionWithAnimation:[profile getAnimation:@"attack"
                                        index:currentDir]],
			[CCCallFuncND actionWithTarget:attackee 
         selector:@selector(gotHit:data:) data:self],
	nil];
 action.tag = kActionAttack;
[sprite runAction:action];
	}

And I am trying to get this method to fire off bullets.


Code:
-(void) attack:(Character*)attackee
{
	BulletCache* bulletCache;

	// get direction for animation
	[self pointToward:attackee.position];

	// create attack sequence (animation + gotHit callback)
	CCAction* action = [CCSequence actions:
	[CCAnimate actionWithAnimation:[profile getAnimation:@"attack"
                                                                                                                                     index:currentDir]],
	[CCCallFuncND actionWithTarget:attackee 
          selector:@selector(gotHit:data:) data:self],
						nil];

	action.tag = kActionAttack;
	[sprite runAction:action];

	if ([self runAction:action] == YES)
	{
		CGPoint shotPos = CGPointMake (self.position.x, self.position.y);
		float spread = (CCRANDOM_0_1() - 0.5f) * 0.5f;
		CGPoint velocity = CGPointMake(4, spread);
		[bulletCache shootBulletFrom:shotPos velocity:velocity 
                            frameName:@"bullet1big.png"
					  isPlayerBullet:YES];
	}

That's what I tried, but I got a Warning: Comparison between pointer and Integer.

Needless to say, when the attack method was called (after hitting the attack button with an enemy in an adjacent tile) the game crashed.

Any tips would be loved. Happy new year!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.