Example:
In an animator there're two states: idle, attack.
In animation transition settings:
Default -> idle
attack(has exit time of 1) -> idle
Goal:
I want to play the attack animation whenever mouse is clicked.
Problem:
Using animator.Play("attack"); will trigger the attack state successfully, however when I clicked mouse immediately, if the animation in attack state has not finished, it won't be triggered again.
I searched for some time, only found this:
http://answers.unity3d.com/questions/787605/mecanim-equvalent-of-animationstop.html
How to solve this? Thanks!
You have an any state in the animator stuff, I think you can just attach your attack state to that. Add a bool to the parameters and have it go from 'any state' to your attack state. You can set the bool in your code. Don't forget to also add an exit to idle.
So it would look like this:
any state -> attack -> idle <- default
Or something like that
More on the any state.
One idea to solve this is to call Play("idle") before Play("attack"). The state would transit into idle then immediately transit into attack. However the following piece of code doesn't work.
public void PlayAttackAnimation()
{
animator.Play("idle");
animator.Play("attack");
}
I've worked a way to make it work as follows.
bool playAttack = false;
public void PlayAttackAnimation()
{
animator.Play("idle");
attackAnimFlag = true;
}
void LateUpdate()
{
if (attackAnimFlag)
{
attackAnimaFlag = false;
animator.Play("attack");
}
}
This works in Unity 5.2.2f1, but feels kind of hack.
Notice: It only works using LateUpdate(), if you change the second part as Update(), it won't work. I don't know why, guessing Unity must have done some thing related with animator state changing between Update() and LateUpdate().
Related
I found time to investigate a bit into QT, and it is very interesting for me. However, right now I am encountering a problem that I am not aware about how to solve it. My aim is actually simple. I have a QCheckBox that I want to activate. If it is activated, I am starting a process (I am opening a file, reading it, taking some values out and change different labels accordingly). This process is repeated until the user is deactivating the QCheckBox. Some small code example to get a better idea of what I am going to do.
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
qDebug() << "Start data analysis";
// Infinity loop to get the data and display it
while true
{
// Open file and extract data
const actualTemperature = getData();
// Change any label or do something with the data
ui->anyLabel->setText(actualTemperature);
// Some break
QThread::sleep(1);
// Leave the loop if user deactivate the QCheckBox
// Something like on_actualTemperature_stateChange == 0
}
}
// Stop reading the data
else
{
qDebug() << "Stop data analysis";
}
}
It is obvious that after activating the QCheckBox, the loop will not finish at all and the GUI will not recognize anything anymore. Hence, I guess I have to start some new thread and have to kill it. However, I have no idea how to proceed here. An idea would be:
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
// Start reading the file and updating the label using some other thread
startThread(XY);
}
// Stop reading the data
else
{
// Kill thread 1234
killThread(XY);
}
}
Any hint is warmly welcomed and I hope this question is not too basic for you. Thank you for reading, Tobi.
I think killing a running thread is not a decent behavior. Let's be gentle to our threads with a loop control variable. In this example it named keepLoop. Set keepLoop when checkbox checked. Then start thread if it is not running. We are using QtConcurrent::run, and monitoring it by a QFuture in this case.
connect(ui->checkBox, &QCheckBox::toggled,
[&](const bool checked) {
analyzer->keepLoop = checked;
if (checked && !future.isRunning())
future = QtConcurrent::run(analyzer, &Analyzer::on_actualTemperature_stateChanged);
}
);
Don't call user interface slots directly, instead connect them to signals. Connections will be queued connection when signals emitted from another thread. It means slots will be called in event loop of main thread and changes will be shown when the next frame painted.
connect(analyzer, &Analyzer::temperatureCalculated, ui->anyLabel, &QLabel::setText);
Our asynchronous function does not forced to die immediately when user toggle checkbox. Instead we letting it to finish the iteration it already on halfway through.
Analyzer::on_actualTemperature_stateChanged() {
while (keepLoop) {
// Open file and extract data
const QString& actualTemperature = getData();
// send data
emit temperatureCalculated(actualTemperature);
}
}
You can use atomic bool if you want a more precise loop control.
Bonus:
If you don't want to mess with threads, you can avoid GUI freezing by using QTimer to run your loop periodically in main thread.
I am working with SerialDevice on C++/winrt and need to listen for data coming over the port. I can successfully work with SerialDevice when data is streaming over the port but if nothing is read the DataReader.LoadAsync() function hangs even though I set timeouts through SerialDevice.ReadTimeout() and SerialDevice.WriteTimeout(). So to cancel the operation I am using IAsyncOperation's wait_for() operation which times out after a provided interval and I call IAsyncOperation's Cancel() and Close(). The problem is I can no longer make another call to DataReader.LoadAsync() without getting a take_ownership_from_abi exception. How can I properly cancel a DataReader.LoadAsync() call to allow subsequent calls to LoadAsync() on the same object?
To work around this, I tried setting the timeouts of SerialDevice but it didn't affect the DataRead.LoadAsync() calls. I also tried using create_task with a cancellation token which also didn't allow for an additional call to LoadAsync(). It took a lot of searching to find this article by Kenny Kerr:
https://kennykerr.ca/2019/06/10/cppwinrt-async-timeouts-made-easy/
where he describes the use of the IAsyncOperation's wait_for function.
Here is the initialization of the SerialDevice and DataReader:
DeviceInformation deviceInfo = devices.GetAt(0);
m_serialDevice = SerialDevice::FromIdAsync(deviceInfo.Id()).get();
m_serialDevice.BaudRate(nBaudRate);
m_serialDevice.DataBits(8);
m_serialDevice.StopBits(SerialStopBitCount::One);
m_serialDevice.Parity(SerialParity::None);
m_serialDevice.ReadTimeout(m_ts);
m_serialDevice.WriteTimeout(m_ts);
m_dataWriter = DataWriter(m_serialDevice.OutputStream());
m_dataReader = DataReader(m_serialDevice.InputStream());
Here is the LoadAsync call:
AsyncStatus iainfo;
auto async = m_dataReader.LoadAsync(STREAM_SIZE);
try {
auto iainfo = async.wait_for(m_ts);
}
catch (...) {};
if (iainfo != AsyncStatus::Completed)
{
async.Cancel();
async.Close();
return 0;
}
else
{
nBytesRead = async.get();
async.Close();
}
So in the case that the AsyncStatus is not Completed, the IAsyncOperation Cancel() and Close() are called which according to the documentation should cancel the Async call but now on subsequent LoadAsync calls I get a take_ownership_from_abi exception.
Anyone have a clue what I'm doing wrong? Why do the SerialDevice timeouts not work in the first place? Is there a better way to cancel the Async call that would allow for further calls without re-initializing DataReader? Generally, it feels like there is very little activity in the C++/winrt space and the documentation is severely lacking (didn't even find the wait_for method until about a day of trying other stuff and randomly searching for clues through different posts) - is there something I'm missing or is this really the case?
Thanks!
Cause: When the wait time is over, the async object is in the AsyncStatus::Started state. It means that the async object is still running.
Solution: When you use close() method, you could use Sleep(m_nTO) let asynchronous operation have enough time to close. Refer the following code.
if (iainfo != AsyncStatus::Completed)
{
m_nReadCount++;
//Sleep(m_nTO);
async.Cancel();
async.Close();
Sleep(m_nTO);
return 0;
}
given this code:
void FooBar::ProcessExitHandler(QProcess* someProcess, QString logsPath)
{
if (clientProcess->exitCode() != 0)
{
QMessageBox* dialog = new QMessageBox();
dialog->setText("bye bye");
dialog->setStandardButtons(0);
QObject::connect(dialog, &QMessageBox::finished, [this](int) {
if (mMainWindow->AutoCloseCheckBoxChecked())
{
delete dialog; //TODO: need to confirm what is the correct way
this->quit();
}
});
dialog->show();
dialog->activateWindow();
}
else
{
if (mMainWindow->AutoCloseCheckBoxChecked())
{
delete dialog; //TODO: need to confirm what is the correct way
this->quit();
}
}
}
Is calling delete dialog like that correct? Is there a more QT idiomatic way of doing this?
Also, something that has caused me confusion is the idea (from the docs) that I should be passing a parent to the constructor of the message box. Then I would get automatic memory management, right? Is that the QT style I should shoot for?
I'm aware that since the app is exiting anyway, the leak "doesn't matter", but I want to do the right thing.
The right way is to use setAttribute
QMessageBox* dialog = new QMessageBox();
dialog->setAttribute(Qt::WA_DeleteOnClose);
By setting the attribute WA_DeleteOnClose, the destructor will be called at the right moment.
When you manually call delete, the pointer will keep its value (the address) although it isn't valid anymore. If for some reason you were to reuse that pointer again, the app would crash.
\warning Deleting a QObject while pending events are waiting to be
delivered can cause a crash. You must not delete the QObject directly
if it exists in a different thread than the one currently executing.
Use deleteLater() instead, which will cause the event loop to delete
the object after all pending events have been delivered to it.
https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qobject.cpp.html#881
I'm using PNaCl and I'm in a situation where first I receive a message that is handled in the 'HandleMessage' function as the normal way, but then in the current HandleMessage execution, I need to wait for a user input that would come from an other message in order to complete the execution.
I'm wondering if this is possible to do that (handling a message while already waiting in the 'HandleMessage' function) ? And if so, can someone give me a trick ?
Thanks !
HandleMessage is currently called on one thread, the main thread. So you cannot receive a message while you are handling another message.
We typically suggest you spawn a new thread to do your work, and leave the main thread to handle messages, and queue them for the new thread to handle. Take a look at the nacl_io_demo example in the SDK for an example of this technique (found in examples/demo/nacl_io).
Another solution is to use a state machine; i.e. keep track of your current state in a variable instead of on the stack.
For example:
enum State {
STATE_INIT,
STATE_WAITING_FOR_INPUT,
STATE_DO_OTHER_STUFF,
};
State state_;
virtual void HandleMessage(const pp::Var& var_message) {
switch (state_) {
case STATE_INIT:
if (var_message.AsString() == "first_message") {
state_ = STATE_WAITING_FOR_INPUT;
// Do some work before you need the user input ...
}
break;
case STATE_WAITING_FOR_INPUT:
if (var_message.AsString() == "user_input") {
// Do more work, now that we've received input from the user...
state_ = STATE_DO_OTHER_STUFF;
}
break;
}
}
In an AIR application I have the following code:
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
theDialog.addEventListener(FlexEvent.CREATION_COMPLETE, cpuIntensiveCalc);
At the end of cpuIntensiveCalc the dialog is removed. The dialog informs the user that "something is going on, please stand by."
The problem is that cpuIntensiveCalc starts before the dialog draws. So the user experience is that the application freezes for 10 seconds with no indicator, then the modal dialog flashes quickly (less than a second) on screen.
The Adobe docs say this about creation_complete
Dispatched when the component has finished its construction,
property processing, measuring, layout, and drawing.
So this feels like the correct event.
In the name of completeness, I also tried
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
cpuIntensiveCalc();
But had the same results.
TIA
The reason for this is that the Flash Player is single threaded, and so you are blocking the UI from reacting to the Dialog Popup until the maths chunk is finished.
Hacky fix time...
You have two options.
(This one should work, but is untested) Wrap the cpuIntensiveCalc() call in a callLater, so that the UI can finish rendering before you block the rendering.
Or
Use "Green Threads" to break up your processing so that you don't completely block the UI processing. Take a look.
(I just had the same issue => even if this thread is old, I just wanted to contribute my solution)
(disclaimer: this is a bit ugly, but they say that's ok in the UI layer... ;-) )
Flex is single threaded (at least from our developer's perspective, I think behind the scene threads are used by the VM)
=> you typically execute your code in the UI thread, after the user did some action on a widget. Any call to update a UI component (like setProgress or setLabel) will only be rendered on screen at the end of the render cycle (see again UiComponent life cycle).
=> In therory calling "cpuIntensiveCalc" in a callLater will let the framework display your popup before executing the method.
In practice though, I noticed you typically have to have for a couple of UI cylces before the popup be displayed, like this:
new MuchLaterRunner(popup, 7, cpuIntensiveCalc).runLater();
MuchLaterRunner being defined like this:
public class MuchLaterRunner
{
var uiComp:UIComponent;
var currentCounter = 0;
var cyclesToWaitBeforeExecution=0;
var command:Function;
public function MuchLaterRunner(uiComp:UIComponent, cyclesToWaitBeforeExecution:uint, command:Function)
{
this.uiComp = uiComp;
this.command = command;
this.cyclesToWaitBeforeExecution =cyclesToWaitBeforeExecution;
}
public function runLater() {
currentCounter ++;
if (currentCounter >= cyclesToWaitBeforeExecution) {
uiComp.callLater(command);
} else {
// wait one more cycle...
uiComp.callLater(runLater);
}
}
}
The issue is the same when calling setProgress afterward: we must divide cpuIntensiveCalc into small callable methods that can be ran at each UI cycle, otherwise the progressbar won't, err, progress.
Use enterFrame event on popup. Don't forget to remove the listener in the enterFrame event handler - otherwise the cpu intensive method will be called in each frame, crashing your app. If this doesn't work at first, use a private number as a counter and keep incrementing it in the enter frame handler - call cpu heavy method only when the counter reaches the appropriate value. Find the 'appropriate' value by trail and error.
theDialog = PopUpManager.createPopUp(this, TheDialogClass, true) as TheDialogClass;
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(e:Event):void
{
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}
//in case the above method doesn't work, do it the following way:
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private var _frameCounter:Number = 0;
private function onEnterFrame(e:Event):void
{
_frameCounter++;
var desiredCount:Number = 1;//start with one - increment until it works.
if(_frameCounter < desiredCount)
return;
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}