I am using cocos2dx v3.11.1 with c++ to create simple game for iOS platform. But I run into strange issue with sprite movement.
Definition of main objects looks like that:
Sprite *logoBackSprite;
SpriteFrameCache *cache;
SpriteBatchNode *batchNode;
I create sprite from the plist like that:
cache = SpriteFrameCache::getInstance();
cache->addSpriteFramesWithFile("game_sprites1.plist");
batchNode = SpriteBatchNode::create("game_sprites1.png");
addChild(batchNode);
logoBackSprite = Sprite::createWithSpriteFrameName(fileName);
logoBackSprite->setPosition(Vec2(centerPoint.x-4, centerPoint.y + 200-43));
addChild(logoBackSprite, 0);
After that I call function which must perform simple moving animation for sprite. Code of this function looks like that:
void MainMenuScene::playMenuAnimations() {
float startDelay = 0.0f;
auto windowSize = Director::getInstance()->getVisibleSize();
Point centerPoint = Vec2(windowSize.width/2, windowSize.height/2);
logoBackSprite->stopAllActions();
logoBackSprite->setOpacity(0);
logoBackSprite->runAction(Sequence::create(DelayTime::create(startDelay + 0.5 + 0.75),
FadeTo::create(0.5f, 255),
nullptr));
}
But when I run application - nothing happens. Could you point me what I am making wrong? Thank you.
EDIT After few hours of experiments, I have noticed, that runAction method of sprite objects doesn't work. For example, I have created vector of sprite frames and I want to run animation. When I run application I can see this sprite, but animation still does not work. Maybe, I missed smth important?
EDIT2
Today I have noticed that any action works prefect on previous scene.
Issue solved. Problem was in bad configuration of scene where I want to run animations.
I should override onEnter() and onExit() methods. In onEnter() method of scene I call
Layer::onEnter();
Also in scene class I create init() method like that:
bool MainMenuScene::init() {
if ( !Layer::init() )
{
return false;
}
return true;
}
And animation runs perfectly!
Related
As far as I've found out, cocos doesn't offer a simple filter handling like AS3 for example does.
My situation:
I want to add a realtime shadow to an cocos2d::Sprite.
For example I would like to do something like this (similar to AS3):
auto mySprite = Sprite::createWithSpriteFrameName("myCharacter.png");
DropShadowFilter* dropShadow = new DropShadowFilter();
dropShadow->distance = 0;
dropShadow->angle = 45;
dropShadow->color = 0x333333;
dropShadow->alpha = 1;
dropShadow->blurX = 10;
dropShadow->blurY = 10;
dropShadow->strength = 1;
dropShadow->quality = 15;
mySprite->addFilter(dropShadow);
This should add a shadow to my Sprite to achieve an result like this:
Adobe Drop Shadow Example
Could you help me please?
There isn't any built in support for shadows on Sprites in Cocos2D-X.
The best option, performance-wise, would be to place your shadows in your sprite images already, instead of calculating and drawing them in the code.
Another option is to sub-class Sprite and override the draw method so that you duplicate the sprite and apply your effects and draw it below the original.
One possible way to achieve that is with this snippet from this thread on the Cocos forum. I can't say that I completely follow what this code does with the GL transforms, but you can use this as a starting point to experiment.
void CMySprite::draw()
{
// is_shadow is true if this sprite is to be considered like a shadow sprite, false otherwise.#
if (is_shadow)
{
ccBlendFunc blend;
// Change the default blending factors to this one.
blend.src = GL_SRC_ALPHA;
blend.dst = GL_ONE;
setBlendFunc( blend );
// Change the blending equation to thi in order to subtract from the values already written in the frame buffer
// the ones of the sprite.
glBlendEquationOES(GL_FUNC_REVERSE_SUBTRACT_OES);
}
CCSprite::draw();
if (is_shadow)
{
// The default blending function of cocos2d-x is GL_FUNC_ADD.
glBlendEquationOES(GL_FUNC_ADD_OES);
}
}
I'm trying to develop a game in cocos2d-x and want one image on the scene to be repeated 10 times in one row. Can a do it in a function and call it again using setPosition wherever I want that image?
Suppose the function name is point() where I have defeinition:
auto sprite = Sprite::create("point.png");
and function returns the sprite.
So can I create a sprite in it using that function and call it like
point()->setPosition(40, 40);
Sprite* GenerateScene::point()
{
auto sprite6 = Sprite::create("point.png");
sprite6->setAnchorPoint(Vec2(0.0, 0.0));
return sprite6;
}
bool GenerateScene::init()
{
auto sprite = Sprite::create("bkgnd.png");
sprite->setAnchorPoint(Vec2(0.0, 0.0));
sprite->addChild(point());
sprite->setPosition(0, 0);
point()->setPosition(120, 480);
}
This piece of code here only generates an "point.png" at (0, 0) location of the background image i.e. bottom left corner. It is not considering
point()->setPosition(120, 480);
statement.
Thanks
The purpose of a Sprite is to repeat an image on the screen.
If you looked at the Sprite::create source you would see that creating 10 sprites with the same image does not create and load 10 images. Instead there is a SpriteFrameCache that caches the actual image. Each of the 10 Sprites would be just a lighweight reference to an instance of that one image in the Scene.
I've been having a lot of trouble implementing a smooth and reliable touch-and-hold solution for firing bullet (sprites) - even after looking at other people's solutions.
The solution has to switch between touch-began, touch moved and touch ended seamlessly: always firing bullets at the touch location until the finger is released. At the moment I have many problems with reliability and stability with every case but touchmoved, which works fine.
The exact issue is around half the time a finger is held down (touchBegan + scheduler) the bullets appear but disappear a second later but other times they move towards the touch perfectly - something is deleting them and i don't have much experience with schedulers or actions to know what.
Heres my code, I've used 2 different firing methods: one scheduled to run every 0.05 seconds after touchBegan, and one triggered every time touchMoved is detected. The touchMoved one works fine, but getting it to work with the unreliable touchBegan one is trouble. The really annoying part is even if I remove the touch part and just schedule sprites to appear and run scheduled actions non-stop from init, the same reliability problem happens (disappearing/deletion). Maybe I dont understand getting schedulers and actions to play nice, or maybe theres a better touch and hold method? Thanks in advance for any help.
bool HelloWorld::init()
{
... miscellaneous sprite creation
this->schedule(schedule_selector(HelloWorld::fireBullets), 0.05);
}
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); // get single-touch as opposed to multitouch
touchLocation = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
if (touchLocation.x > 400)
{
float dX = touchLocation.x - gun->getPosition().x;
float dY = touchLocation.y - gun->getPosition().y;
touchAngle = atan2(dY, dX);
gun->setRotation(-CC_RADIANS_TO_DEGREES(touchAngle));
cursor->setPosition(touchLocation);
screenHeld = true;
}
}
void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); // for single-touch as opposed to multitouch
touchLocation = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
if (touchLocation.x > 400)
{
float dX = touchLocation.x - gun->getPosition().x;
float dY = touchLocation.y - gun->getPosition().y;
float angle = atan2(dY, dX);
gun->setRotation(-CC_RADIANS_TO_DEGREES(angle));
cursor->setPosition(touchLocation);
screenHeld = false; //not technically true but touchMoved bullet firing works differently (not scheduled, every movement instead)
if (getTimeTick() - lastBulletFire > 50) //getTickTime is simple get system time method, works fine
{
fireBullet(angle);
}
}
}
//this is for touchBegan and has issues, scheduled to run every 50ms touch-held
void HelloWorld::fireBullets(CCTime dt)
{
if (screenHeld)
{
CCSprite* bullet = CCSprite::create("bullet.png");
bullet->setPosition(ccp(gun->getPosition().x, gun->getPosition().y));
bullet->setRotation(-CC_RADIANS_TO_DEGREES(touchAngle));
//send bullet towards touchlocation
bullet->runAction(CCSequence::create(CCMoveBy::create(1.5f, ccp(800 * cos(touchAngle), 800 * sin(touchAngle))), NULL));
this->addChild(bullet, 5);
}
}
//this is for touchMoved and works fine, everytime finger is moved bullet fired
void HelloWorld::fireBullet(float angle)
{
CCSprite* bullet = CCSprite::create("bullet.png");
bullet->setPosition(ccp(gun->getPosition().x, gun->getPosition().y)); //add a random spread to the y value (or maybe the y-value of the destination)
this->addChild(bullet, 5);
bullet->setRotation(-CC_RADIANS_TO_DEGREES(angle));
bullet->runAction(CCSequence::create(CCMoveBy::create(1.5f, ccp(800 * cos(angle), 800 * sin(angle))), NULL));
lastBulletFire = getTimeTick();
}
Found the solution, it wasn't anything to do with actions/schedulers but the fact that I wasn't calling retain() on the bullets. Normally I'd manage C++ memory manually but this hybrid style threw my off
I have a few layers, which holds CCSprites .
I have a main CCScene that is adding these layers at the start.
I have to check collision between sprites from different layers .
I know how to check a simple collision with CGRectContains , my problem is , that i need some kind of a class that holds all the sprites positions from all layers ,and to check each tick for collision.
Question: what is the right way to create such a class,that save/check sprites from all layers on the scene ?
THanks .
If you don't care about empty space, then you can just set self.contentSize for every layer. You can do it like this:
CGRect rect = CGRectNull;
for (CCNode *node in self.children) {
rect = CGRectUnion(rect, node.boundingBox);
}
self.contentSize = rect.size;
But if you care about empty space, then you need to use something like this:
// put it somewhere outside class implementation
static inline BOOL CCLayerIntersectsCCLayer(CCLayer l1, CCLayer l2) {
for (CCNode *n1 in l1.children) {
for (CCNode *n2 in l2.children) {
CGRect r1 = n1.boundingBox;
CGRect r2 = n2.boundingBox;
r1.origin = [l1.parent convertToNodeSpace:r1.origin];
r2.origin = [l2.parent convertToNodeSpace:r2.origin];
if (CGRectIntersectsCGRect(r1, r2)) {
return YES;
}
}
}
return NO;
}
But you must understand, that all sprites (even circles) are actually rectangles. So if you need very precise collision detection, then you should use box2d in your project. If you are interesting in this, here are some links to read:
Box2d
Intro to Box2D with Cocos2D 2.X Tutorial: Bouncing Balls
Box2D Tutorial for iOS: How To Use Box2D For Just Collision Detection with Cocos2D iPhone
I have one question when infinite background scrolling is done, is the object remain fixed(like doodle in doodle jump, papy in papi jump) or these object really moves.Is only background move or both (background and object )move.plz someone help me.I am searching for this solution for 4/5 days,but can't get the solution.So plz someone help me. And if object does not move how to create such a illusion of object moving.
If you add the object to the same layer as the scrolling background, then it will scroll as the background scrolls.
If your looking for an effect like the hero in doodle jump, you may want to look at having two or more layers in a scene.
Layer 1: Scrolling Background Layer
Layer 2: Sprite layer
SomeScene.m
CCLayer *backgroundLayer = [[CCLayer alloc] init];
CCLayer *spriteLayer= [[CCLayer alloc] init];
[self addChild:backgroundLayer z:0];
[self addChild:spriteLayer z:1];
//Hero stays in one spot regardless of background scrolling.
CCSprite *squidHero = [[CCSprite alloc] initWithFile:#"squid.png"];
[spriteLayer addChild:squidHero];
If you want objects to scroll with the background add it to the background layer:
//Platform moves with background.
CCSprite *bouncePlatform= [[CCSprite alloc] initWithFile:#"bouncePlatform.png"];
[backgroundLayer addChild:bouncePlatform];
Another alternative is to use a CCFollow action. You would code as if the background is static (which it will be) and the player is moving (which it will be), but add a CCFollow action to the player. This essentially moves the camera so that it tracks your player.
You can also modify the classes so that you can get the CCFollow action to follow with an offset (i.e., so the player is not in the middle of the screen) as well as to have a smoothing effect to it, so that when the player moves, the follow action is not jerky. See the below code:
*NOTE I am using cocos2d-x, the c++ port. The methods are similar in cocos2d, and you should be able to modify these to fit the cocos2d syntax. Or search around -- I found these for cocos2d and then ported to c++.
//defines the action to constantly follow the player (in my case, "runner.p_sprite is the sprite pointing to the player)
FollowWithOffset* followAction = FollowWithOffset::create(runner.p_sprite, CCRectZero);
runAction(followAction);
And separately, I have copied the class definition for CCFollow to create my own class, CCFollowWithAction. This also has a smoothing effect (you can look this up more online) so that when the player moves, the actions are not jerky. I modified "initWithTarget," to take into account an offset, and "step," to add a smoothing action. You can see the modifications in the comments below.
bool FollowWithOffset::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
CCAssert(pFollowedNode != NULL, "");
pFollowedNode->retain();
m_pobFollowedNode = pFollowedNode;
if (rect.equals(CCRectZero))
{
m_bBoundarySet = false;
}
else
{
m_bBoundarySet = true;
}
m_bBoundaryFullyCovered = false;
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);
//m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);
m_obHalfScreenSize = CCPointMake(m_obFullScreenSize.x/2 + RUNNER_FOLLOW_OFFSET_X,
m_obFullScreenSize.y/2 + RUNNER_FOLLOW_OFFSET_Y);
if (m_bBoundarySet)
{
m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);
m_fRightBoundary = -rect.origin.x ;
m_fTopBoundary = -rect.origin.y;
m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);
if(m_fRightBoundary < m_fLeftBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
}
if(m_fTopBoundary < m_fBottomBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
}
if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
{
m_bBoundaryFullyCovered = true;
}
}
return true;
}
void FollowWithOffset::step(float dt)
{
CC_UNUSED_PARAM(dt);
if(m_bBoundarySet){
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(m_bBoundaryFullyCovered)
return;
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary),
clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
}
else{
//custom written code to add in support for a smooth ccfollow action
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
CCPoint moveVect = ccpMult(ccpSub(tempPos,m_pTarget->getPosition()),0.25); //0.25 is the smooth constant.
CCPoint newPos = ccpAdd(m_pTarget->getPosition(), moveVect);
m_pTarget->setPosition(newPos);
}
}