Animation using .png files cocos2dx - c++

I have 34 .png image files for 9 different scenarios. Each time the user picks 1 of those 9 scenarios and according to that I generate animation using the following
std::string name;
MixingScreen::animation = CCAnimation::create();
// load image file from local file system to CCSpriteFrame, then add into CCAnimation
for (int i = 0; i < 34; i++)
{
std::stringstream st,ii;
st << Constants::flav;
if (i<10)
{
ii<<i;
name="screen/screen02/fx/sugar/0" + st.str()+"/a_0000"+ii.str()+".png";
}
else
{
ii<<i;
name="screen/screen02/fx/sugar/0" + st.str()+"/a_000"+ii.str()+".png";
}
//sprintf(szImageFileName, "Images/grossini_dance_%02d.png", i);
MixingScreen::animation->addSpriteFrameWithFileName(name.c_str());
}
MixingScreen::animation->setDelayPerUnit(5.0f / 34.0f);
action = CCAnimate::create(MixingScreen::animation);
CCCallFunc *done = CCCallFunc::create(this, callfunc_selector(MixingScreen::doneMixing));
CCSequence *readySequence = CCSequence::create(action,done,NULL);
particles->runAction(readySequence);
The problem I am facing is that when this animation runs, there is a time lag(everything stops for few seconds) and then the animation starts. Any solution?

Every time you call animation->addSpriteFrameWithFileName() a new "CCSpriteFrame" is created.
Instead you should first add the sprite frames to CCSpriteFrameCache and use
animation->addSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameWithName(""))
Ps: these function names might be a little different but i hope you get the idea.

Related

Pause/Resume Action/Animation on Sprite in Cocos2d

Using Cocos2d-x and C++, I'm trying to play and pause an animation for a sprite.
I'm using version 3.15.1 of Cocos2dx.
I have a class called PlayerSprite which is derrived from the cocos2d::Sprite class. Inside PlayerSprite initialization, I've setup my animation with the following code:
SpriteBatchNode* playerSpriteBatch = SpriteBatchNode::create("player.png");
SpriteFrameCache* spriteFrameCache = SpriteFrameCache::getInstance();
spriteFrameCache->addSpriteFramesWithFile("player.plist");
Vector<SpriteFrame*> animFrames(2);
char str[18] = { 0 };
for (int i = 1; i < 3; i++) {
sprintf(str, "player_idle_%d.png", i);
SpriteFrame* frame = spriteFrameCache->getSpriteFrameByName(str);
animFrames.pushBack(frame);
}
Animation* idleAnim = Animation::createWithSpriteFrames(animFrames, 0.8f);
self->idleAction = self->runAction(RepeatForever::create(Animate::create(idleAnim)));
self->idleAction->setTag(0);
When I run the code, it works fine and the animation loops correctly.
In my void update() method, I am trying to pause/play the action/animation based of weather the player is moving or idle.
I do this with the following code:
const bool isIdleActionRunning = this->getNumberOfRunningActionsByTag(0) > 0 ? true : false;
const bool isMoving = !vel.isZero();
if (!isMoving && !isIdleActionRunning) {
// Player is idle AND animation is not running
// Run animation
this->runAction(idleAction);
} else if (isMoving && isIdleActionRunning) {
// Player is moving but animation is running
// Pause animation
this->stopActionByTag(0);
}
When I run this code now, my character falls, and as soon as he hits the gound, I get an error at this->runAction(idleAction); saying:
Access violation reading location 0xDDDDDDE5
I believe this is caused due to this->stopActionByTag(0) deleting the action pointer. I've tried to clone the action to avoid this but have had no success.
I know this is a bit late and you might already have solved this but here goes...
Your problem is that you cannot use one instance of Action (idleAction) multiple times. So, once you have run it and removed it, it is released and cannot be used. So, you have 2 options now,
Either create a new idleAction Action every time before running the action.
Or, have an idleAction retained and don't run it ever. Instead, create a clone of this idleAction and run a new clone each time. i.e.
idleAction->retain();
const bool isIdleActionRunning = this->getNumberOfRunningActionsByTag(0) > 0 ? true : false;
const bool isMoving = !vel.isZero();
if (!isMoving && !isIdleActionRunning) {
// Player is idle AND animation is not running
// Run animation
Action idleActionClone = idleAction->clone();
this->runAction(idleActionClone);
} else if (isMoving && isIdleActionRunning) {
// Player is moving but animation is running
// Pause animation
this->stopActionByTag(0);
}
Solution: call retain() to keep your action.
It's a matter of memory management of cocos2d-x.
In create() function of your RepeatForever class (derived from Ref), the reference count is set to 1 and there is a line of code ret->autorelease() before returning the object, which means this object will be released automatically at the end of this frame.
You called runAction() function the same frame you created it, the action is retained by ActionManager, it's reference count set to 2, and then 1 at the end of the frame (autorelease).
After your stopping it, it's released by ActionManager, reference count set to 0 and it's deleted. After this you use your action, it will be an access violation method.
*Edit: don't forget to release the action manually when PlayerSprite is deleted, or it's a leak.
When you stop action it's being recycled from memory. In order to play action once more, you have to recreate it. So you may just make a creator function, which returns your animation. The downside is you're recreating animation each time and it'll also play from the beginning (technically you can rewind it).
But I've developed a simpler technique to pause/resume animations:
Let's say you have an action:
action = MoveBy::create(5.0f, Vec2(50, 100));
Now, you can embed this action into Speed action like this:
action = Speed::create(MoveBy::create(5.0f, Vec2(50, 100)), 1.0f);
1.0f - is speed, so it's normal action rate. Now to pause just call:
action->setSpeed(0.0f);
and to resume:
action->setSpeed(1.0f);
you can also use different speed if you need it for some reason or another ;)

After adding some points to QtChart it gets slow

I need help. Here is my code (i show main part):
// Arduino Reader
serial.setPortName("/dev/ttyACM0");
serial.open(QIODevice::ReadWrite);
serial.setBaudRate(QSerialPort::Baud9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
connect(&serial,SIGNAL(readyRead()),this,SLOT(getNewData()));
...
void MainWindow::getNewData()
{
std::clock_t starter = std::clock();
QByteArray data = serial.readAll();
QDataStream in(serial.readAll());
getData = getData + data;
int start = getData.indexOf(":START:");
int end = getData.indexOf(":END:",start);
QString nowWork = getData.mid(start,end-start+5);
if (nowWork.startsWith(":START:") && nowWork.endsWith(":END:")){
QStringList mod = nowWork.remove(":START:").remove(":END:").split(":");
int xPoint = mod[0].toInt();
int yPoint = mod[1].toInt();
int zPoint = mod[2].toInt();
int aPoint = mod[3].toInt();
int bPoint = mod[4].toInt();
int cPoint = mod[5].toInt();
addQuery(1,xPoint,yPoint,zPoint);
addQuery(2,aPoint,bPoint,cPoint);
getData = getData.right(end+5);
}
std::clock_t ender = std::clock();
ui->ping->setText(QString::number( (ender-starter)/ (double)CLOCKS_PER_SEC *100));
return;
}
...
void MainWindow::addQuery(int to, int x, int y, int z){
seriesAllD1->append(now, (x+y+z)/3 );
seriesXD1->append(now,x);
seriesYD1->append(now,y);
seriesZD1->append(now,z);
seriesAllD1->remove(0);
seriesXD1->remove(0);
seriesYD1->remove(0);
seriesZD1->remove(0);
chartAllD1->axisX()->setRange(now-100,now);
chartX->axisX()->setRange(now-100,now);
chartY->axisX()->setRange(now-100,now);
chartZ->axisX()->setRange(now-100,now);
now++;
return;
}
I try to describe code:
1) It gets data from arduino (Like a ":START:X:Y:Z:A:B:C:END::START:...").
2) It gets indicates from 2-sensors (3-axis accelometr). And draws Graphs.
I show some code block. After adding ~900 point it gets slow from 60 ms to 1000 ms. I think it is related to QtChart (i use them in project) or addind points to series. Please help me(
This is an old topic - however I also ran into it when working with QtCharts - so: for others who will stumble on the same problems with QtCharts and would also like to use QtCharts here are my solutions:
Use OpenGL:
call setUseOpenGL(true) on the series in the plot (will only work with QLineSeries and QScatterSeries) - unfortunately this is not the default, but it could be a problem on some systems
When not using OpenGL (i.e. also using QAreaSeries):
do not use AntiAliasing
when adding large amounts of data in one loop to the series use rather replace than add - see https://stackoverflow.com/a/52334549/2056545
And this special trick a colleague came up with when working with real time data:
set all series to signalsBlocked(true), start a timer when blocking, unblock after a second, block again and start timer when adding new data again - this will update the plots on (about) every second, but the performance impact is low

SFGUI rendering issue c++

I'm making a server menu system however when I remove all the items from the sfgui system and move on to another game state the labels from the previous game state are visible under a circumstance which I will explain in a minute, first let me show you the issue.
Server Menu:
Here you can see the issue:
The code for removal is as follows.
void S_ServerMenu::Exit() {
ServerSelectWindow->Show(false);
desktop.RemoveAll();
desktop.Refresh();
}
However this issue only occurs on refresh of servers here is the code for refresh.
void S_ServerMenu::RefreshServers() {
Document d;
d.Parse<0>(LoadInServers().c_str());
servers = ServerParser(d);
ServerListTable->RemoveAll();
ServerListTable->RefreshAll();
for(int i = 0; i < servers.size(); i++) {
auto label = sfg::Label::Create();
label->SetText(servers[i].Name);
MenuItem utm;
utm.lbl = label;
utm.index = i;
utm.owner = this;
label->SetAlignment(sf::Vector2f(0, 0));
label->FontSize = 16;
label->SetParent(ServerListTable);
label->cont = ServerSelectWindowContainer;
ServerListTable->Attach(label, sf::Rect<sf::Uint32>(1, i, 1, 1), sfg::Table::FILL | sfg::Table::EXPAND);
label->GetSignal(sfg::Label::OnLeftClick).Connect(std::bind(&MenuItem::Clicked, utm));
}
ServerSelectWindow->RefreshAll();
}
Do any of you know how to solve this if so that would be great.
I fixed this by creating a separate Desktop for each screen.

How to refresh sprites (remove and recreate it again)

I am trying to make a day-night background mode in my game and I want to create a control button in a option dialog that when I click on it, all background are change without exiting the dialog. I have just made it run OK by re-open the scene but it also quit the option dialog.
I have an initBackground() method like this
void MenuScene::initMenuBackground() {
setBackgroundMode();
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
//calculate system hour time
time_t now = time(0); // get time now
tm * ltm = localtime(&now);
double hour = ltm->tm_hour;
int curHourTime = (int) hour;
CCLOG("MenuScene hour is: %dh", curHourTime);
CCAnimation* caveAnim1 = CCAnimation::createWithSpriteFrames (senspark::Utils::createFramesArray("cave-%d.png", 3), 0.2f);
CCAnimation* caveAnim2 = CCAnimation::createWithSpriteFrames(senspark::Utils::createFramesArray("cave-%d.png", 3, 0, true), 0.2f);
cloudSpr->runAction(CCRepeatForever::create(CCSequence::create(CCMoveTo::create(30, ccp(winSize.width+100, cloudSpr->getPositionY())),
CCMoveTo::create(0, ccp(-100, cloudSpr->getPositionY())),
NULL)));
cloudNightSpr->runAction(CCRepeatForever::create(CCSequence::create(CCMoveTo::create(30, ccp(winSize.width+100, cloudSpr->getPositionY())),
CCMoveTo::create(0, ccp(-100, cloudSpr->getPositionY())),
NULL)));
//night
if ( ((curHourTime < 6 || curHourTime > 18) && (_isAuto==true)) || _isNight==true) {
caveNightSpr->runAction(CCRepeatForever::create(CCSequence::create(CCAnimate::create(caveAnim1),
CCAnimate::create(caveAnim2),
CCDelayTime::create(0.2f),
NULL)));
cloudSpr->setVisible(false);
startGoldSpr->setVisible(false);
backgroundSpr->setVisible(false);
backgroundSkySpr->setVisible(false);
backgroundNightSpr->setScaleX(CCDirector::sharedDirector()->getWinSize().width/designSize.width);
backgroundSkyNightSpr->setScaleX(CCDirector::sharedDirector()->getWinSize().width/designSize.width);
}
//daytime
if ( ((curHourTime > 6 && curHourTime < 18) && (_isAuto==true)) || _isDay==true) {
caveSpr->runAction(CCRepeatForever::create(CCSequence::create(CCAnimate::create(caveAnim1),
CCAnimate::create(caveAnim2),
CCDelayTime::create(0.2f),
NULL)));
cloudNightSpr->setVisible(false);
startGoldNightSpr->setVisible(false);
backgroundNightSpr->setVisible(false);
backgroundSkyNightSpr->setVisible(false);
backgroundSpr->setScaleX(CCDirector::sharedDirector()->getWinSize().width/designSize.width);
backgroundSkySpr->setScaleX(CCDirector::sharedDirector()->getWinSize().width/designSize.width);
}
and I don't know how to refresh these Sprite (remove and then recall them again).
Sorry for my bad English. Any help would be appreciated.
From what i understand from your question, you need to remove sprite from the scene and add it again.
CCNode/Sprite has this method removeFromParentAndCleanup(bool cleanup)
Also you can remove all child from a CCNode/Sprite with
removeAllChildrenWithCleanup(bool cleanup).
Node/Sprite can be assigned special setTag(), which can used later to remove special Sprite with removeChildWithTag(). People generally have enums to identify key elements.
I hope that answers your question.
Refrences:
http://www.cocos2d-x.org/reference/native-cpp/V2.2.3/d9/d1f/group__base__nodes.html#ga5889948f4c40933e93aaf70cb8846192
Finally figure out my issue, it's very simple that I can change the sprite image by using mySprite->setTexture(CCTextureCache::sharedTextureCache()->addImage("newImage.png"));
no more need to remove and then add it again.

Thread loading images faster until they disapear in Studio

I am pretty new to visual studio. I am trying to load images and display it on a UI with two buttons start and stop. Every time I complete my count of total frames the next time I press start my images load more faster than before and in the end images the speed is so fast that they just disappear or appear as black. The thread speed or loading of images time increases as with each cycle. How can I limit this.. or put a time interval between showing two images. Thank you.
there code is as follows :
void CmirrorImageDlg::OnBnClickedStart()
{
m_play = TRUE;
CString num = NULL;
num.Format(_T("Pause"));
m_start.EnableWindow(false);
m_stop.EnableWindow(true);
m_stop.SetWindowTextW(num);
m_pThread = AfxBeginThread(Operate, (LPVOID)this, THREAD_PRIORITY_NORMAL);
}
void CmirrorImageDlg::OnBnClickedStop()
{
m_play = FALSE;
CString num = NULL;
num.Format(_T("Resume"));
m_start.EnableWindow(true);
m_stop.EnableWindow(false);
m_start.SetWindowTextW(num);
m_count = 0;
//m_pThread = AfxBeginThread(Operate, (LPVOID)this, THREAD_PRIORITY_ABOVE_NORMAL);
// TODO: Add your control notification handler code here
}
UINT CmirrorImageDlg::Operate(LPVOID param)
{
CmirrorImageDlg* pDlg = (CmirrorImageDlg*) param;
CString test;
while ( pDlg->m_play && pDlg->m_count < TOTAL_FRAME_NUMBER)
{
test.Format(_T("images/%.4d.BMP"),pDlg->m_count);
pDlg->hbitmap = (HBITMAP)LoadImage(NULL,test,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
pDlg->inv_hbitmap = pDlg->GetInvertedBitmap(pDlg->hbitmap, TRUE);
pDlg->inv_hbitmap_1 = pDlg->GetInvertedBitmap(pDlg->hbitmap , FALSE);
//CBitmap map = LoadBitmapW(test);
//filePath = IDB_BITMAP1;
//filePath++;
//IDB_PICTURE2.LoadBitmapW(filePath);
pDlg->m_picture.SetBitmap(pDlg->hbitmap);
pDlg->m_picture_2.SetBitmap(pDlg->inv_hbitmap);
pDlg->m_picture_3.SetBitmap(pDlg->inv_hbitmap_1);
CString num = NULL;
num.Format(_T("%d"),pDlg->m_count);
pDlg->m_label.SetWindowTextW(num);
// TODO: Add your control notification handler code here
pDlg->m_count++;
}
if(pDlg->m_count >= TOTAL_FRAME_NUMBER)
{
CString num = NULL;
num.Format(_T("%d"),0);
pDlg->m_count = 0;
pDlg->m_play= false;
pDlg->m_label.SetWindowTextW(num);
pDlg->m_picture.SetBitmap(NULL);
pDlg->m_picture_2.SetBitmap(NULL);
pDlg->m_picture_3.SetBitmap(NULL);
CString num1 = NULL;
num.Format(_T("Stop"));
pDlg->m_start.EnableWindow(true);
pDlg->m_stop.EnableWindow(false);
pDlg->m_stop.SetWindowTextW(num);
num.Format(_T("Start"));
pDlg->m_start.SetWindowTextW(num);
//pDlg->m_pThread->SuspendThread();
}
return 0;
}
A reasonably simple option:
Set a WM_TIMER for your dialog to fire every 40 milliseconds (if you want 25 frames per second). If not playing, the timer handler will do nothing.
When the user hits play, you'll load the first image, and then set the 'playing' flag. Inside the timer handler, it will take the currently loaded image and blit it to the screen, then load the next image and return. When there are no more images in the sequence you clear your 'playing' flag.
Because you always display the current image when the timer fires, it doesn't matter how long it takes to load the next image (as long as you can do it faster than the playback).