Stars for scrolling shooter with CCParticleSystemQuad? - cocos2d-iphone

My code for init function:
NSArray *starsArray = [NSArray arrayWithObjects:#"Stars1.plist", #"Stars2.plist", #"Stars3.plist", nil];
for(NSString *stars in starsArray) {
CCParticleSystemQuad *starsEffect = [CCParticleSystemQuad particleWithFile:stars];
[self addChild:starsEffect z:-1];
}
The problem is that these particles appear and fully fill the screen rectangle during few seconds. But I need the sky full of stars from the beginning.

According to the answer at cocos2d starting particles from a specific time in the future , you can manually update the particle system. Example in cocos2d-x:
CCParticleSystemQuad *particle = CCParticleSystemQuad::create("res/Particles/Stars1.plist");
for (int i = 0; i < 10; ++i) {
particle->update(.1);
}
You may need to change the interval to suit the particles.

Add them to a layer, hide the layer, then unhide the layer after everything is done loading. That way you can set stuff up and not have it display right away.
That's just one approach. Another idea is to load all your images into Cocos before game play and game logic processes begin. That way there is no pause and delay while images are loading.

Related

How do you load a scene while animating a sprite in cocos2d-x?

I have a "stage selection" scene and a "game" scene. However when the user presses the button to start the game scene there is a delay between the pressing and the scene showing (about 2 seconds or more on older devices). So what i thought is i should create a loading scene.
So what i am doing right now is passing to my "Loading" scene a std::function which gets called 0.1 seconds after the loading scene appears. This function has the code to start the "game" scene like this:
For creating the loading scene.
auto loading_scene = LoadingScene::createLoadingScene([stage_course]() {
Director::getInstance()->replaceScene(Game::createScene(stage_course->course_id));
});
Director::getInstance()->replaceScene(loading_scene);
To load the game scene.
void LoadingScene::onEnter()
{
Node::onEnter();
call_after(0.1, callback_start);
}
The result of this is the loading scene showing with a simple animated sprite of a character running. At first i tried with a delay of 1.0 seconds before the callback to check that the sprite is working correctly (it does, the character runs). But it stops moving when the callback is executed (the one that loads the new scene) and remains like this for about 1-2 seconds until the scene has loaded and then its presented..
Does anyone know how to keep the sprite animated while the scene is getting load so that it never stops running until the "game" scene is shown?
Edit:
I am using cocos2d-x-3.8.
My loading scene has the following code in its init function to create an animation which is used to animate a spire:
// Create the sprite animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
string frame_sprite_name = StringUtils::format("Interface/loading/0_%d.png",i);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);
if (frame) {
animation->addSpriteFrame(frame);
} else {
break;
}
}
animation->setDelayPerUnit(0.15f);
Animate *animate = Animate::create(animation);
// Create a temporal sprite to run the animation
auto temp_sprite = Sprite::create();
temp_sprite->setAnchorPoint(Vec2(0.5,0.5));
temp_sprite->setPosition(Vec2(DISPLAY_WIDTH/2.0f,DISPLAY_HEIGHT/2.0f));
this->addChild(temp_sprite);
temp_sprite->runAction(RepeatForever::create(animate));
Edit 2:
The reason why my game scene takes too much time to load is because i am loading all the spritemaps my stage needs like this:
// Shared
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(STUDENTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(STUDENTS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(OTHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(OTHERS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(INTERFACE_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(INTERFACE_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(ZOMBIES_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(ZOMBIES_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(PORTRAITS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(PORTRAITS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(CUTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(CUTS_SPRITE_MAP);
}
// Exclusive
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(TEACHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(TEACHERS_SPRITE_MAP);
}
Try this:
void HelloWorld::loadTextures1(){
Director::getInstance()->getTextureCache()->addImageAsync("sprites1.png", CC_CALLBACK_1(HelloWorld::loadTextures2, this));
}
void HelloWorld::loadTextures2(Texture2D* texture1){
this->texture1 = texture1;
CCLOG("Texture 1 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites2.png", CC_CALLBACK_1(HelloWorld::loadTextures3, this));
}
void HelloWorld::loadTextures3(Texture2D* texture2){
this->texture2 = texture2;
CCLOG("Texture 2 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites3.png", CC_CALLBACK_1(HelloWorld::allTexturesLoaded, this));
}
void HelloWorld::allTexturesLoaded(Texture2D* texture3){
this->texture3 = texture3;
CCLOG("Texture 3 loaded!");
auto cache = SpriteFrameCache::getInstance();
cache->addSpriteFramesWithFile("sprites1.plist", texture1);
cache->addSpriteFramesWithFile("sprites2.plist", texture2);
cache->addSpriteFramesWithFile("sprites3.plist", texture3);
auto scene = GameScene::createScene();
Director::getInstance()->replaceScene(TransitionShrinkGrow::create(.5, scene));
}
It asynchronously loads these 3 textures and then synchronously load plist files with preloaded textures. There's a bit of freeze on the end (synchronous part), but it's not bad I think. You may also dig deeper into SpriteFrameCache and try to optimise it.
I am not aware that cocos2d-x supports this particularly well:
CSLoader loads the scene on the current thread so it blocks the animation loop until its finished.
To achieve smooth animations wile CSLoader is working it would need to be refactored either
* to run in a worker thread - Which is going to be hard as (last time I looked) any Ref derived object is going to panic if its not created on the main cocos thread.
* to frequently callout to the Dispatcher / runloop to allow a frame to animate. I havn't looked at the cocostudio::CSLoader code to see how amenable it would be to this... it certainly doesn't seem to support this out the box.
The alternative that might be hard to achieve is to simply break you scene up into chunks - each of which can be quickly loaded so there is no discernible load delay.
I agree with what Chris is saying, specifically off-loading work or breaking things into chunks.
True, Cocos2d-x is not designed for multi-threading, so that's why you need to implement an update callback that does only a small amount of work, then waits till the next runloop to allow the engine to render.
I wish I can help you solve the loading issues, but the code you are showing is only for loading the animations, which isn't really the problem. We need to see the code that is causing the slow down to provide some practical solutions.

cocos2D: Adding delay after rendering a scene

I am trying to launch a physics object after loading a game scene, something similar to https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/creating-two-levels/
The code I have is something like this
- (void)didLoadFromCCB {
// tell this scene to accept touches
self.userInteractionEnabled = TRUE;
[self launchObject];
}
- (void) launchObject {
CCNode* object = [CCBReader load:#"Object"];
// Apply force to it
}
The problem is if I add a sleep method in didLoadFromCCB before launching the object or the first line in the launchObject, the game scene itself is loading only after that many seconds (say after clicking play) and immediately launches, but I want the game scene to load and after n seconds the physics object is launched.
I can easily solve this problem by using
- (void)update:(CCTime)delta
by setting some conditions for launch, but the question is, is that the right approach. I don't want to complicate update method to hold multiple if/else stuff and use it beyond what it's intended for, especially if there is another best way to do it.
I tried different solutions posted in this forum but didn't help my case.

Cocos2d animations in main loop

Anyone that can give any hint of the smartest way to do a main loop animation? I don't want to use CCAnimation because I want to control the animations frame by frame.
Shall I store the sprite rect (relative to the sprite sheet) for each individual frame in an array, and then look up the suiting rect in each animation step? I tried to find out how this is done in CCAnimation, but I didn't succeed...
How to get the rect for each frame at initialization?
How to set the rect at each animation step?
Do I need to use CCSpriteBatchNode? I guess not, eh?
Cannot crealry understand, why you don't want to use CCAnimation, but anyway, to get answer for your questions you can check creation code of the CCSprite instance. Then, check creation of CCSpriteFrame instance. There you will find the answer for at least your first question.
Actually if you just want to manage animation frames differently from CCAnimate, you can just store array of CCSpriteFrames and show them as you want(in CCAnimate action these frames are just changed one by one in equal time intervals).
And if you do not want to show more than one frame of your animation, there is no difference will you use CCSpriteBatchNode or not. It saves a lot of processor time if you need to draw several parts of one texture, as it draws them in one draw call instead of send draw message to all of these sprites.
As you want animate sprite frame by frame I think using CCSpriteBatchNode would be a better option as it give you frame by frame access of animation.Making plist of sprites using any tool like "Zwoptex" will give an efficient way to animate using CCSpriteBatchNode.
Hope you know the animation using plist file with CCSpriteBatchNode.
I did the following with inspiration from Morions answer:
In the game tick function:
_animationFrames.legFrame = (_animationFrames.legFrame + 1) % _animationFrames.legFrames.count;
[_legs setDisplayFrame: [_animationFrames.legFrames objectAtIndex: _animationFrames.legFrame]];
And in the init function:
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode
batchNodeWithFile:#"Player.png"];
[self addChild:spriteSheet];
_animationFrames.legFrames = [[NSMutableArray array] retain];
for(int i = 0; i <= 15; ++i)
{
[_animationFrames.legFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"Player_legs-%d.png", i]]];
}
_legs = [CCSprite spriteWithSpriteFrameName:#"Player_legs-0.png"];
[_sprite addChild: spriteSheet];
[spriteSheet addChild:_legs z:1];

Drawing clouds like shapes in cocos2D

Im new to cocos2D
I would like to draw cloud like particles similar to the one in the attached figure as one drags across the screen. I tried using CCParticleSmoke but I couldn't control the spreading of the particle. Initialization:
smoke = [[CCParticleSmoke alloc] initWithTotalParticles:100];
smoke.texture = [[CCTextureCache sharedTextureCache] addImage:#"cloud.png"];
smoke.gravity = CGPointZero;
smoke.startColor = _color;
smoke.posVar = CGPointZero;
In gesture method
smoke.position = pointPosition;
[smoke visit];
When I run my code, what happens is perpendicular cloud like particles appear though I dragged parallel. Cant get any clues about what to do.
I don't want particles to spread, but as in the below image. Any timely help is appreciated.
Thanks.

Cocos2d iPhone. Scrollayer contentSize. Layers

I want to set the contentsize of scrollayer.
I have a scrollayer, it's CCLayer type and moving is set by ccTouchMove. I have one schedule for smoothing. BUT.
Problem is that scrolling layer is big like the whole display. I want to set the contentsize of scrollayer. Content in this layer will be scroll and showing ONLY in this layer. Not taking up the whole display. Something like this
Scrolling just in gray CCLayer (scrollayer) ....NOT WHOLE SCREEN.
Can you help me?
P.S.: Setting CCLayerColor and initWithColor:Width:Height: is not working. It just makes some stupid color box and it's moving too.
ok, honestly i would put the window frame at a higher z than the scrolling object ... if you dont you may have to crop and change sprite on the fly for the window content, nasty (at least that is the one way i could do this, without further research).
so :
// initialize this logic somewhere useful
CCNode scrollableContent;
CCSprite windowFrame;
BOOL isScrollPossible;
[self addChild:scrollableContent z:0];
[self addChild:windowFrame z:1];
// and in the touch delegate methods
-(void) ccTouchBegan:{
check if the touch happened in the window, if yes, scrolling is possible
}
-(void) ccTouchMoved:{
if (isScrollPossible) {
compute displacement
compute nextPosition for scrollableContent node;
if ( nextPosition in window ) {
// make scrollableContent follow touch
scrollableContent.position=nextPosition;
} else {
// stop any further scrolling until the next 'touch began'
isScrollPossible=NO;
}
} else {
// scroll not possible, do nothing
}
}
This is the basic idea. You may need clamping logic to prevent the creeping of scrollableContent beyond the edges of the window.
Edited for typos.
After trying desperately with masking and GL_SCISSOR, I settled on cutting a hole in the foreground and only moving the background object when onTouchMoved updated.
To see it in action, have a look at the Stats page of Angry Gran.