In my game, which is using cocos2d, there is going to be many different types of enemies, which all look different, and move all in different ways. Also, there is going to be a couple of different gamemodes, which both use the same enemies. As there will be different gamemodes, I decided to make each of my enemies have their own CCSprite class. In those there will be the way that the sprites move, the animation, etc. When one of these sprite is needed in my game, they will be spawned in to the scene. The only thing is, how do I do this? How do I call for one of the sprites to be create on the screen when they are using a class of their own?
If you want to tell me another way than having these sprites having their own classes, that is fine, but keep in mind that I will be having a couple of different gamemodes. If I do the code for the sprites in the CCLayer class of that gamemode, well I will have to write the code twice, which will take time.
Thanks.
You can just subclass CCSprite and override the default initializer initWithTexture:rect:
example taken from here
#implementation MySprite
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
{
if( (self=[super initWithTexture:texture rect:rect]))
{
// initialize your ivars here
//ivar1 = xxx;
//ivar2 = yyy;
//ivar3 = zzz;
}
return self;
}
#end
// And to create an instance of MySprite you simply do:
MySprite *sprite = [MySprite spriteWithFile...];
// or any of the supported CCSprite methods.
you can have a super class say EnemySprite that looks like this
#interface EnemySprite : CCSprite
- (void)addToLayer:(CCLayer *)layer;
- (void)removeFromLayer:(CCLayer *)layer;
#end
than create a subclass for each type of enemy for example:
#inteface BigEnemySprite : EnemySprite
#end
#implementation BigEnemySprite
- (void)addToLayer:(CCLayer *)layer {
[layer addChild:self];
// animation code for your big enemy
}
- (void)removeFromLayer:(CCLayer *)layer {
[layer removeChild:self];
// animation code
}
#end
than you can use them like
EnemySprite *enemy = [BigEnemySprite spriteFromFile:file];
[enemy addToLayer:self];
Related
I have a custom class "LinkWithNumber" with three sprites
and on the GameLayer in update I am trying to test
CGRectContainsRect for collisions but I am having trouble trying to access the sprite in the class file (I don't have much experience so most likely I am messing up :P)
I have tried the following:
LinkWithNumber.h
#interface LinkWithNumber : SKSpriteNode <SKPhysicsContactDelegate>
{
SKSpriteNode *collide;
}
LinkWithNumber.m
#synthesize collide;
//add collision object to the class
collide = [[SKSpriteNode alloc]initWithColor:[SKColor blueColor]
...blah blah as normal
[self addChild:collide];
collide.name = #"collide";
GameLayer.h
#class LinkWithNumber;
#interface GameScene : SKScene <SKPhysicsContactDelegate>
{
LinkWithNumber* twoSpritesWithParticlesBridge;
}
#property (nonatomic, strong)LinkWithNumber* twoSpritesWithParticlesBridge;
GameLayer.m
#synthesize twoSpritesWithParticlesBridge;
-(void)addStaticLinkedSpriteWithParticles
{
twoSpritesWithParticlesBridge =
[[LinkWithNumber alloc]initWithlinkSpriteA:#"RoseMine06"
spriteB:#"RoseMine06"
andPlistAnimation:#"need to create animations"
distbetween:300
hasParticles:YES
ParticlesNamed:#"Fire"];
[self addChild:self->twoSpritesWithParticlesBridge];
twoSpritesWithParticlesBridge.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: twoSpritesWithParticlesBridge.frame.size];
}
-(void)update:(CFTimeInterval)currentTime {
LinkWithNumber *currentSprite =
(LinkWithNumber*)[self childNodeWithName:#"collide"];
//NSLog(#"currentSprite Name #%#", currentSprite); //gets nil
if (CGRectContainsRect(myShip02.frame,currentSprite.frame)) {
NSLog(#"Hit barrier can pass");
}
}
Any help would be appreciated :)
How to locate your class object... The Solution, with thanks to 0x141E!
//name it on setup inside your customCLass
//eg yourObject.name = #"collide";
//Now in Gamelayer locate your object by recursive search
//it will look for any object named #"//collide"
//without the slashes it will only look on the game layer
//but since we need to dig a bit further we need them!
LinkWithNumber *currentSprite =
(LinkWithNumber*)[self childNodeWithName:#"//collide"];
NSLog(#"LinkWithNumber is %#",NSStringFromClass([currentSprite class]));
//do something with your object
if (currentSprite.position.y >0 ) {
NSLog(#"currentSprite Position %f",currentSprite.position.y);
}
extras
Sknode Class ref for other search functions
How to enumerate all nodes
I see two issues...
You are searching only the root node (in this case the scene) for the node named "collide", but that node is a child of LinkWithNumber node not the scene. To recursively search the entire node tree, use #"//collide"
You are casting the result of the search to a LinkWithNumber pointer, but collide is an SKSpriteNode not a LinkWithNumber.
I have a class called Blade.h and I make A SKSpriteNode on that!
now I make new instance of that class on Main Class.
#import "Blade.h"
#implementation Blade
-(void) GenerateBalde
{
SKSpriteNode *blade = [SKSpriteNode spriteNodeWithImageNamed:#"blade"];
[blade runAction:[SKAction rotateByAngle:M_PI duration:1]];
[self addChild:blade];
}
#end
now, I want to have an instance of this object in my main class and I wanna detect the physic collision the Blade with another Sprites in my main class!
I know how to make an instance , but I wanna know how to detect the collision of different objects in different classes + physic!
please advice!
It would be better if your Blade was a subclass of SKSpriteNode and then in its init method use:
if (self = [super initWithImageNamed:#"blade"]){
//more code
}
return self;
Another solution is to use a property, so instead of creating a SKSpriteNode in generateBlade method use this:
In Blade.h add #property (nonatomic, retain) SKSpriteNode *bladeSprite.
In your generateBlade method, use this:
self.bladeSprite = [SKSpriteNode spriteNodeWithImageNamed:#"blade"];
[self.bladeSprite runAction:[SKAction rotateByAngle:M_PI duration:1]];
[self addChild:self.bladeSprite];
Then, in your MainScene, in update: method, you can check the collision using this code:
-(void)update:(CFTimeInterval)currentTime {
if (CGRectIntersectsRect(otherObject.frame, blade.bladeSprite.frame){
//Collisioning!
}
}
If you are using a SKSpriteNode subclass as mentioned in first lines, you could just do blade.frame instead of blade.bladeSprite.frame (you won't need to set a property).
I am trying to make a game that consist of a "hero" that runs endlessly and basically has to avoid obstacles and as he avoids these obstacles he receives point and I am trying to make this game in Sprite Builder which I am not so good in yet and having a hard time coding for it.
Basically this is what I have so far:
static const CGFloat scrollSpeed = 0.1;
#implementation level
{
CCPhysicsNode *_physicsNode;
CCSprite *_guy;
}
- (void)update:(CCTime)delta
{
_guy.position = ccp(_guy.position.x + delta * scrollSpeed, _guy.position.y);
_physicsNode.position = ccp(_physicsNode.position.x - (scrollSpeed *delta), _physicsNode.position.y);
}
- (void)didLoadFromCCB
{
// tell this scene to accept touches
self.userInteractionEnabled = TRUE;
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
//whenever the screen is tapped, this will happen
[self guyJump];
}
- (void)guyJump {
//makes the guy jump
}
But then I am also receiving this error in the console:
CCBReader: Couldn't find member variable: _physicsNode
CCBReader: Couldn't find member variable: _guy
HOW I SOLVED IT !
Make sure that in SpriteBuilder:
level is the custom class of your CCScene root node
Your CCSprite and CCPhysicsNode is marked as Doc root var and the correct name is
marked, also it should not be a custom class
Do a File->Clean Cache
Press the Publish button
Make sure that in XCode
You have a custom class called level
In level you have an ivar declared
#implementation level
{
CCPhysicsNode *_physicsNode;
CCSprite *_guy;
}
Do a Product->Clean
Run your project
Try to replace these lines of code:
#implementation level
{
CCPhysicsNode *_physicsNode;
CCSprite *_guy;
}
with:
#interface level ()
{
CCPhysicsNode *_physicsNode;
CCSprite *_guy;
}
#implementation level
Make sure that in SpriteBuilder:
level is the custom class of your CCScene root node
Your CCSprite and CCPhysicsNode is marked as Doc root var and the correct name is
marked, also it should not be a custom class
Do a File->Clean Cache
Press the Publish button
Make sure that in XCode
You have a custom class called level
In level you have an ivar declared
#implementation level
{
CCPhysicsNode *_physicsNode;
CCSprite *_guy;
}
Do a Product->Clean
Run your project
I have a "bomb" CCSprite in its own class(If people who doesn't use cocos2d reads this, CCSprite is a NSObject pretty much).
The CCSprite file looks like this:
Bomb.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import <OpenAL/al.h>
#class HelloWorldLayer;
#interface Bomb : CCSprite {
#private
int length;
}
#property (readwrite) int length;
#end
Bomb.m:
#import "Bomb.h"
#implementation Bomb
#synthesize length = _length;
#end
I add it in my game layer (HelloWorldLayerlike a pro) with #class Bomb; in .h, and I import the Bomb.h in my HWLayer.m aswell and where I use it in my code, is here:
Bomb *bombe = [[Bomb alloc] init];
bombe.position = explosionPoint;
bombe.length = player.explosionLength; //player is another CCSprite class. This one is from the method. ....fromPlayer:(PlayerSprite *)player
//Logging here works, tested and the bombe.position is valid and .length is valid
[currentBombs addObject:bombe];
NSLog(#"%#",currentBombs); //Here doesn't, guessing crash is at ^
As said, it crashes on the addObject:line. I can't really see why, as I just replaced a not-classed CCSprite with a Bombclass.
The crash is just a (lldb) and the thing on the left outputs a few thousand of these:
Which says description, so I would assume its in my CCSprite subclass the error is. BUT the bombe.* logging worked fine!
Does anyone understand why it doesn't work?
Edit:
EDIT:
NSLog(#"%#",currentBombs); //Here doesn't, guessing crash is at ^
Your %# implies NSString. currentBombs is likely an int. Try
NSLog(#"%i",currentBombs); //Here doesn't, guessing crash is at ^
CCSprite requires a texture. You can (maybe?) have a CCSprite without one, but that's not what CCSprite is for.
You will want to use CCNode for this purpose:
CCNode* node = [CCNode new];
This is a full-fledged Cocos2d object that you can move, etc. You'd add your Bomb to it, and move the CCNode around, like this:
Bomb *myBomb = [Bomb new]; //or whatever
CCNode* bombNode = [CCNode new];
//add the bomb to the node
[bombNode addChild:myBomb];
//move the node
bombNode.position = CGPointMake(10, 20)
This allows you to remove the myBomb from your node, effectively having something that you can add whatever you want without needing to display anything, but when you want to, it can be done easily.
Good luck
try this method :
Bomb *bombe = [Bomb spriteWithFile:#"yourFile.png"];
bombe.position = explosionPoint;
bombe.length = player.explosionLength;
[currentBombs addObject:bombe];
NSLog(#"%#",currentBombs);
I'm trying to learn Cocos2D from Learning Cocos2D, A hands-on guide by Rod Strougo & Ray Wenderlich, but it uses Cocos 1, not 2 which is out now. I imagine the book is still relevant later on, but in the first chapter I'm running into an issue telling the director to run a scene, because it seems like that whole process is now different in Cocos2D 2.
I'd rather not have to buy a different book, so I was hoping that changing the way to run a scene is fairly simple.
This is what the book says to do:
find the -applicationDidFinishLaunching method and comment out:
[[CCDirector sharedDirector]runWithScene: [HelloWorld scene]];
and add:
[[CCDirector sharedDirector]runWithScene:[GameScene node]];
I can't find anything that looks like that in AppDelegate, instead it looks like this has something to do with the new way:
[director_ pushScene: [IntroLayer scene]];
My attempts to adapt what the tutorial says to the new way has so far failed, but maybe it is an easy fix.
Incase it is the GameScene which is outdated:
GameScene.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "BackgroundLayer.h"
#import "GameplayLayer.h"
#import "CCScene.h"
#interface GameScene : CCScene
{
}
#end
GameScene.m
#implementation GameScene
-(id)init
{
self = [super init];
if (self != nil)
{
BackgroundLayer *backgroundLayer = [BackgroundLayer node];
[self addChild:backgroundLayer z:0];
GameplayLayer *gameplayLayer = [GameplayLayer node];
[self addChild:gameplayLayer z:5];
}
return self;
}
#end
The issue you are having is with the class method +scene. That is used when creating the scene within a layer instead of initializing a scene and having that instance create its own child layers. You will understand the differences later in the book when you get more into the scene -> layer relationship.
Comment out [director_ pushScene: [IntroLayer scene]]; in -applicationDidFinishLaunching and replace it with the following:
[director_ pushScene:[GameScene node]];
That should work just fine for your needs. It will create an instance of GameScene (subclass of CCScene) with your two CCLayer subclass instances as children, those being the backgroundLayer and gameplayLayer you instantiate in the GameScene -init method.
If you are curious as to why calling [GameScene scene] was not working for you, that is because you never declared such a method in your interface. It is a little confusing, but basically you would instead create a Game Layer subclass of CCLayer and in your .h file, declare this class method:
+ (CCScene *)scene;
In your implementation .m file, you would define that method as such:
// Class initializer method
+ (CCScene *)scene {
CCScene *scene = [CCScene node]; // Create a container scene instance
GameLayer *gameLayer = [GameLayer node]; // Create an instance of the current layer class
[scene addChild:gameLayer]; // Add new layer to container scene
return scene; // Return ready-made scene and layer in one
}
Then when you call [GameLayer scene] that +scene method creates the scene for that layer and adds it as a child. It is quicker, but can be more troublesome with multiple layers in my experience.
Have you tried this:
[[CCDirector sharedDirector] pushScene:[IntroLayer scene]];