Invoke a method only once from a class - c++

I have a shape class in which there is a method (hitTest(int,int)) that continuously checks if the mouse is inside its bounds or not. In another method, I keep on checking if the mouse has stayed there for more than 1 sec.
If it has, trigger a function (by notification/event) that runs animation
It it has not, then don't trigger the animation
If it has already triggered the animation and the animation is running but the mouse leaves the area during this, trigger an interrupt function (by notification/event)
//OnLoad _initHover = false;
void update() //called continously in the application per frame
{
if(hitTest(getMouseX(), getMouseY())){
if(!_initHover){
_initHover = true;
_hoverStartTime = getCurrentTime(); //start hover time
cout<<"Start hist test\n";
}
//If it has hovered over the video for 1.0 sec
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0){
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
}
}
else{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
}
}
The above code runs fine but there's a logical issue I am facing while trying to use. There are multiple instances of the above Shape class and each class consequently has their update() method as well. The mouse cursor has which has animationStarthandler and animationStophandlers is a single class in the whole application.
Issue 1: So, even when one of the shape just notifies the animationStarthandler to fire, the other shape classes on which hit test is false set the animation to interrupt and the animation does not run.
Issue 2: When the hit test succeeds and the cursor has been in the area for more than 1 sec, the hit test will keep on sending the notification to start the animation (anim's duration 1.5 sec approx.) How do I restrict the hit test to fire the animation only once and keep on firing the same animation again and again?
If in the main method of my application, I directly try to fire the animation by calling the method playAnimation in the pointer class, I get the required result. But I want to give this hover timing and animation functionality to the ShapeClass itself. Any suggestions?

I think that you should consider adding a new boolean, which holds the information of the triggering of the animation (called in the code sample _animationTriggered). This prevents shapes that have not triggered the animation to stop it and the animation that triggered it to make it several times.
if(hitTest(getMouseX(), getMouseY()))
{
if(!_initHover)
{
_initHover = true;
_hoverStartTime = getCurrentTime();
cout<<"Start hist test\n";
}
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0)
{
if (!_animationTriggered)
{
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
_animationTriggered = true;
}
}
}
else
{
if ( _animationTriggered )
{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
_animationTriggered = false;
}
}
Don't forget to initialie this new boolean in the same place as _initHover

Related

Updating QChart from QLineSeries in a running while loop

I want to make my QChart dynamically update whenever a point is added to the QLineSeries object attached to it, but it seems that this update only occurs after the while loop I am running has finished. I am using said while loop in interface.cpp that calls a function updatePlot() which adds the data point to the line series, but this only updates the chart after the while loop has completely finished. Pseudo code of what is happening here:
qtwindow.cpp
// Constructor that initializes the series which will be passed into the interface
AlgoWindow::AlgoWindow( ..., TradingInterface* interface, ... ) {
...
QLineSeries* series = new QLineSeries();
QLineSeries* benchmark = new QLineSeries();
QChart* chart = new QChart();
chart->addSeries(series);
chart->addSeries(benchmark);
// Also creates custom axes which are attached to each series
...
}
// Slot connected to a button signal
void AlgoWindow::buttonClicked() {
// Runs the backtest
interface->runbacktest(..., series, benchmark, ...);
}
interface.cpp
void TradingInterface::runbacktest(..., QtCharts::QLineSeries* algoplot, QtCharts::QLineSeries* benchplot) {
// Runs a huge while loop that continuously checks for events
while (continue_backtest) {
if (!eventsqueue.isEmpty()) {
// Handle each event for the bar
} else {
// All events have been handled for the day, so plot
updatePlot(algoplot, benchplot);
}
}
}
void TradingInterface::updatePlot(QtCharts::QLineSeries *algoseries,
QtCharts::QLineSeries *benchseries) {
// Get the date and the information to put in each point
long date = portfolio.bars->latestDates.back();
double equitycurve = portfolio.all_holdings.rbegin().operator*().second["equitycurve"];
double benchcurve = benchmarkportfolio.all_holdings.rbegin().operator*.second["equitycurve"];
// Append the new points to their respective QLineSeries
algoseries->append(date * 1000, equitycurve*100);
benchseries->append(date * 1000, benchcurve*100);
}
This gives me no errors and the while loop completes, but the lines are only plotted after runbacktest() exits. It then plots all the data correctly, but all at once.
What I need to happen is for the QChart to update every time the lines are added, which my guess was to use some form of custom signal-slot listener but I have no clue how to go about that. If the graph will not update until after the function completes, is it even possible within the QChart framework?
Also, I have already tried QChart::update() and QChartView::repaint(). Both produced the same results as without.
EDIT: I tried setting up a new thread that emits a signal back to the main thread whenever the data is completed but it seems to have changed nothing. The QChart still does not update until after all the data has been inputted. I added a couple lines to help debug and it seems like the function which emits the signal runs consistently just fine, but the slot function which receives the signal only runs after the thread has finished. Not only that, but slowing the signals down with a sleep does not make it plot slowly (like I thought), as the QChart still refuses to update until after the final update to addData().
Either remove your while loop and perform the work one step at a time with a timer.
Or run your runbacktest function in another thread and send a signal to update the QChart in the UI's thread when the data is ready.
Either way you need to give control back to the event loop so that the chart can be repainted.
The Qt idiom for running an operation “continuously” is to use a zero-duration “timer”. It’s not a timer really, but Qt calls it one.
You can do the operation in chunks that take approximately a millisecond. For this, invert the control flow. Qt doesn't provide too much syntactic sugar for it, but it's easy to remedy.
Convert this code, which maintains a loop:
for (int i = 0; i < 1000; ++i) {
doSomething(i);
}
into this lambda, which is invoked by the event loop:
m_tasks.addTask([this](i = 0) mutable {
doSomething(i);
++i;
return i < 1000;
});
assuming:
class Controller : public QObject {
Tasks m_tasks;
...
};
where the Tasks class maintains a list of tasks to be executed by the event loop:
class Tasks : public QObject {
Q_OBJECT
QBasicTimer timer;
std::list<std::function<bool()>> tasks;
protected:
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != timer.timerId())
return;
for (auto it = tasks.begin(); it != tasks.end(); ) {
bool keep = (*it)();
if (!keep)
it = tasks.erase(it);
else
++it;
}
if (tasks.empty())
timer.stop();
}
public:
using QObject :: QObject;
template <typename F> void addTask(F &&fun) {
tasks.emplace_back(std::forward(fun));
if (!timer.isActive())
timer.start(0, this);
}
};

How do I create a 2 task for same function using Qt?

I'm playing around with Qt. I am using one ultrasonic sensor, If I start the toy it works fine.
Currently, What am I doing? For example, I have programmed a toy, I will start the toy then it will start walking continuously, If someone is in front of the ultrasonic sensor, then it will sense that and will stop walking. (This is working fine). This is what I have created.
Toystart Thread:
bool Toystart::Limit()
{
if(Dist <= 55)
{
return true;
}
return false;
}
main Thread:
int sensor = 0;
if (toystart->Limit() )
{
if(sensor == 0)
{
toystart->Stop_talking();
delay (5000);
sensor = 1;
break;
}
}
What do I want exactly?
The above-mentioned code working fine, I want to create a more function. For example some person in front of doll that stops talking 5 seconds then it will check again the still Obst there it will stop talking. It needs to check again after 5 seconds if still Obst there It will go to new function there it will say please leave me then after 5 seconds if Obst not there it will walk.
But it is a blocking call. Your function is stuck for 5 seconds, and depending on how delay() is implemented it might make your whole app unresponsive to any other calls and events.
Pseudocode:
if (obstacle is near) {
if (last_operation == "wait") {
last_operation = "Action";
takeAction();
} else {
last_operation = "wait";
wait();
}
} else { // no obstacle
last_operation = "go";
go();
}
Is that what you're looking after?

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 ;)

QGraphicsscene lags with 150+ items

I am not too familiar with the performance of QGraphicsScene and QGraphicsView.. I know that there is a 10 000 chips example which works well and that the QGraphicsScene is optimized for having a large amount of items in it. However, the game I made lags.( I need to note that this is my first game which actually could be called as such )
My mainloop processing looks like this:
timer.setInterval(0);
timer.start();
connect(&timer, SIGNAL(timeout()), this, SLOT(mainloop()));
inside this mainloop I update all my game logic. Everything seems to be ok, but once I go to fullscreen and I have about 100+ Items in the scene..it starts to lag and the completion of the mainloop takes (sometimes) more than 33ms. Besides the Player, I have only Objects of GameCollectable, you can see the update routine in the code below. Is this because I have my mainloop on the GUI Thread? Because every GameCollectable is rendered by an image? Should I try to make the mainloop in its own thread?
It would be really gentle of you, if someone could take a look at the update routine of my GameCollectable class and tell me why my code produces so much lag. I have a I5 2500K processor btw.
void GameCollectable::update(int t)
{
//animationstuff
if(t>lastframeupdate)
{
if(m_spawner!=NULL)
{
if(animindex>=m_spawner->animation_collectable_normal.get()->animationimgs.size()-1)
animindex=0;
m_graphicsitem->setImage( m_spawner->animation_collectable_normal.get()->getFrameImage(animindex));
lastframeupdate=t;
//animindex++;
}
}
if(started==false)starttime=t;
started=true;
move(t);
}
bool GameCollectable::move(int t)
{
float t_inc=(t-starttime)/animtime;
//reached our destination?
if(m_graphicsitem->pos().x()<destPos.x())
{
onDestination=true;
emit signal_reached(this);
return false;
}
//collided with player?
else if(m_graphicsitem->collidesWithItem(Game::GameManager::instance()->getPlayer()->getGraphicsItem()))
{
Game::SoundManager::instance()->playCoinPickup(t);
bumpedPlayer=true;
emit signal_bumped(this);
return false;
}
else
{
m_graphicsitem->setPos(lerp(startPos.x(),destPos.x(),t_inc),m_graphicsitem->pos().y());
return true;
}
return false;
}

how to force GLUT to redraw the scene before the function returns?

I have an OpenGL project which loads an object file, after the object is loaded i want to be able to move it with keyboard smoothly, so i wrote this block of code to do the job:
while (remains) {
if (x_remains) {
refPosition[0] += speed.x;
if (refPosition[0] > nextPos[0]) {
x_remains = false;
}
}
if (y_remains) {
refPosition[1] += speed.y;
if (refPosition[1] > nextPos[1]) {
y_remains = false;
}
}
if (z_remains) {
refPosition[2] += speed.z;
if (refPosition[2] >= nextPos[2]) {
z_remains = false;
}
}
remains = x_remains || y_remains || z_remains;
glutPostRedisplay();
}
as you see I want to redisplay the scene in the while but when this is executed glutpostredisplay() just sets a flag and the scene is redrawn in the next iteration of the mainloop. my question is how can I redraw the scene before next loop of the while and before the function returns
You won't lose performance or see jerky rendering by going through glutPostRedisplay instead of drawing directly. (Or, if every microsecond really is that precious, why the heck are you using GLUT to begin with?)
On some systems you CAN'T draw outside the display function anyway, because the GL context won't be valid.
This is not unique to GLUT either. All the modern graphics/GUI toolkits I'm familiar with also require you to have a designated display function that gets invoked by an event handler, whether it's WM_PAINT messages, NSView drawRect:, or whatever.
If your program is not updating smoothly, it will be for some other reason.