Qt - Using QEasingCurve's output for something other than an Animation - c++

I wonder could anyone be of help.
If for example I wanted to use the output of the QEasingcurve for another purpose other than driving a QAnimation, is this possible? For example, if I had a numerical read out on my GUI that I wanted to grow, overshoot and bounce back into place could I use the QEasingcurve for this purpose?
I have used Qt for quite a while but have never dabbled with any of these parts of it - I am trying to work it out but cannot, so thought I'd ask here.

I am not sure I understand correctly what you want to display, but from what I understand, using QPropertyAnimation is probably the way to go.
However, to answer your question, you can of course use QEasingCurve in a standalone manner, you just need to use the valueForProgress(qreal progress) member function.

Hey - Just wanted to update with how I carried this out in case anyone looks it up in the future.
void RPM::updateGauge(float speed)
{
easing = new QEasingCurve(QEasingCurve::OutElastic);
easing->setAmplitude(1.0);
currentPosition = (float)ui->svgdial_currentSpeed->value();
newPosition = speed;
difference = newPosition - currentPosition;
interval = 0.0;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(doGaugeMovement()));
timer->start(60);
}
void RPM::doGaugeMovement()
{
interval+=0.01;
ui->svgdial_currentSpeed->setValue(currentPosition + ( difference * easing-
>valueForProgress( interval ) ) );
if(interval >= 1.0)
{
timer->stop();
}
}
Simply used a timer to update the gauge slowly, pulling the valueForProgress result for its new position each time.

Related

How to stop more than one being removed from an integer?

I know the title may seem fairly confusing, was just unsure on how to ask this...
So, I'm working on a basic sample game (not going to be a complete game or anything), where you can move around and are chased by an enemy character that attacks you. The problem is that when the attack function is called, instead of only removing one heart/hitpoint, they continue to be 'spam removed'. Here's what I'm working with...
void Enemy::attackPlayer()
{
if (distance < 50)
{
Player::health--;
return;
}
}
Pretty simple, right? Well the problem is that I need some way of I guess 'sleeping' the single function so that instead of continuing to remove health, it stops after one, then after let's say, 3 seconds, allows another attack to occur.
I think you can create two global time variables that are passed to your attack function. startTime is initiated once you call your attack function (outside). endTime is initiated right after removing one health from player (inside your function). Then you simply add a if statement before the distance if statement to check the delta time between these two and if they are more than 3 seconds then do the rest to remove another health.
You could probably have the Enemy class contain a method like:
bool canAttack(){
if(attackTimer >= 3000){
attackTimer = 0;
return true;
}
return false;
}
Then you could modify your damage condition to be something like:
if (distance < 50 && canAttack())
Of course, you would have to add a timer to the Enemy class and have it start and stop based on proximity to the player.
I'm sure there is a better way to handle this--also, this depends a bit on the implementation of the rest of your code. If you are using something like SFML, there is a built-in event system that would make this a bit easier to handle. Hopefully this helps a bit!
After taking some of the answers you guys gave me into consideration and messing around with some things by myself, I've came up with a pretty simple solution:
int Enemy::attackTime = 0;
And then...
void Enemy::attackPlayer()
{
if (distance > 60)
return;
if (time(0) > attackTime)
{
attackTime = time(0) + 3;
Player::health--;
}
}
I guess, player won't get another attacked from any enemy for 3 seconds. However, enemy can attack to another player if exist. Thus, this timer variable is keep into Player class. If I am correct, I think this code will work.
class Player
{
private:
uint32_t last_attack_timer;
...
public:
void set_last_attack_timer(uint32_t timer){this->last_attack_timer = timer;};
uint32_t get_last_attack_timer(void){return last_attack_timer;};
...
}
void Enemy::attackPlayer()
{
uint32_t timer = time(0);
if (distance < 50 && timer-Player::get_last_attack_timer>3000)
{
Player::health--;
Player::set_last_attack_timer(timer(0));
return;
}
}

Does this Qt code leak memory?

I try to learn more about programming by studying open source using
UML. The code I have found is in the stage between Qt 3 and Qt 4.
The project is not that active so I ask this question here. Maybe
I should add that the program using this code do run.
Note, I am a junior. I ask because I want to learn.
My question is simple:
Do this code leak memory?
If not, why ?
void warn(const QString & s) {
// not showed dialog to compute needed size
QDialog d_aux;
Q3VBoxLayout * vbox_aux = new Q3VBoxLayout(&d_aux);
vbox_aux->setMargin(5);
Q3TextEdit * e = new Q3TextEdit(&d_aux);
e->setText(s);
// showed dialog
QDialog * d = new QDialog;
d->setCaption("My caption");
Q3VBoxLayout * vbox = new Q3VBoxLayout(d);
vbox->setMargin(5);
Q3TextView * t = new Q3TextView(d);
QFontMetrics fm(QApplication::font());
int maxw = (MyWindow::get_workspace()->width() * 4) / 5;
int maxh = (MyWindow::get_workspace()->height() * 4) / 5;
int he = (e->lines() + 5) * fm.height();
t->setText(s);
t->setMinimumSize(maxw, (he > maxh) ? maxh : he);
vbox->addWidget(t);
d->show();
}
Thanks // JG
You have one: The QDialog * d = new QDialog; The other pointers got a parent instance which takes ownership of the pointers. (Please confirm it in the documentation of Q3VBoxLayout, Q3TextEdit and Q3TextView)
I think you are leaking memory, but it would be simple to verify with a program like valgrind on Linux and so on. Let us see what dynamic memory allocations you do in your function:
Q3VBoxLayout * vbox_aux = new Q3VBoxLayout(&d_aux);
That is alright because it gets a parent. Let us see the next:
Q3TextEdit * e = new Q3TextEdit(&d_aux);
That is also alright for the same reason above. Let us see the next:
QDialog * d = new QDialog;
Here, you problems begin to arise because the dialog does not have a parent. You have several ways of fixing it.
1) Assign a parent, although this might not be ideal in this case as you do not seem to have any parent'ish look in your code for this widget. That is, nothing that could really become the parent of it unlike your Qt application if you are using that. This fix may require some major rework depending on the whole context that you have shown.
2) Use a smart pointer, e.g. QPointer around it, so it will be managed for you automatically. That should have been available at the age of the code you are using. This may also need some code rework depending on more context that you have not provided.
Let us see the next dynamic memory allocation:
Q3VBoxLayout * vbox = new Q3VBoxLayout(d);
That is alright for the reasons mentioned before. Let us see the next and last:
Q3TextView * t = new Q3TextView(d);
That is also alright for the reasons mentioned before.

Collision of two sprite lists - SFML 2.0

I am making a simple game in SFML 2 and it came smoothly so far. I created two sf::Sprite lists, one for enemies and one for lasers. The enemies spawn randomly off-screen and the lasers are created whenever input is given. I created a collision loop for both the lists and executed my code. There are no compile time and run time errors. The laser-enemy collision works fine for the first 3 to 4 enemies but after that, the collision does not occur. What might be causing this problem? Please help me on this. Thanks. Here's my code.
std::list<sf::Sprite>::iterator enemyit = enemy.begin(), next;
std::list<sf::Sprite>::iterator greenlaserit = greenlaser.begin(), reload;
while(enemyit != enemy.end())
{
next = enemyit;
next++;
while(greenlaserit != greenlaser.end())
{
reload = greenlaserit;
reload++;
if(enemyit->getGlobalBounds().intersects(greenlaserit->getGlobalBounds()))
{
enemy.erase(enemyit);
greenlaser.erase(greenlaserit);
++erased;
}
greenlaserit = reload;
}
enemyit = next;
}
It seems to be that you are doing a lot of iterator manipulation and that is likely to be where the problem is occurring.
If you can use c++11, I would suggest looking into the for each loop (http://www.cprogramming.com/c++11/c++11-ranged-for-loop.html), to keep things really simple to read and understand (and thus, easier to debug).
You could do something like this:
std::list<sf::Sprite> enemies;
std::list<sf::Sprite> lasers;
for (sf::Sprite enemy: enemies) {
for (sf::Sprite laser : lasers) {
if (enemy.getGlobalBounds().intersects(laser.getGlobalBounds())) {
enemies.remove(enemy);
lasers.remove(laser);
}
}
}
Edit: otherwise, one method I have found for figuring out an iterator problem is stepping through it by hand. I draw two rectangles with cells for each location, and keep track of the iterators and run through the logic step by step. Before each iteration of your logic, write down your expected results. Then go through it by hand and see if your results match your expectations.

Killing the invaders doesn't work in C++

I know that in order to kill invaders in C++, I need to make a collider.
However, nothing will ever kill the invaders in that game.
Here's the code in the header:
bool DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight);
This is the function I'm initializing:
bool Game::DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight) {
if (Xbpos+BulWidth < Xipos || Xbpos > Xipos+InvWidth) return false;
if (Ybpos+BulHeight < Yipos || Ybpos > Yipos+InvHeight) return false;
return true;
}
And this is what happens if somebody presses the space key:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
}
Does anybody know what's going wrong?
The problem isn't C++. The problem is how you are using it. The only way you'll get a kill with your code as written is if the invader is right on top of you. But that's too late. The alien invader has already killed you.
What you need to do is make those bullets into objects that you propagate over time, just like your invaders are objects that you propagate over time. The response to the user pressing a space key should be to add a new instance of a bullet to the set of active bullets. Each of those active bullets has a position that changes with time. On each time step, you should advance the states of the active invaders per the rules that dictate how invaders move and advance the states of the active bullets per the rules that dictate how bullets move. Remove bullets when they reach the top of the screen, and if an alien invader reaches the bottom of the screen, game over.
After propagating, removing off-screen bullets, and checking for game over, you want to check for collisions between each of the N bullets with each of the M invaders. When a collision is detected, remove the bullet from the set of active bullets and delete the alien invader from the set of active invaders. And of course you'll want some nifty graphics to show the user that another alien bit the dust.
Aside: Being an NxM problem, this check might be the biggest drain on CPU usage. You can speed this up with some simple heuristics.
You could manage the collections of alien invaders and bullets yourself, carefully using new and delete so as to prevent your invaders and bullets from killing your program with a memory leak. You don't have to do this. C++ gives you some nifty tools to manage these collections. Use one of the C++ standard library collections instead of rolling your own collection. For example, std::vector<AlienInvader> invaders; or std::list<AlienInvader> invaders, and the same for bullets. You'll be deleting from the middle a lot, which suggests that std::list or std::deque might be more appropriate than std::vector here.
You test the collision for the fired item just when they are created
Shouldn't be the test collision done in the main loop for each existing item at each frame ?
Don't worry, C++ has got all you need to kill invaders :)))
It's not easy to give advice based on so little code, but here the only logical error seems to be you test for collision only when space is pressed; you should test for it in an outside loop probably:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
}
From a logical point of view, pressing Space should fire a bullet: the starting position for the bullet is set, and so is its speed on the Y axis (so that it goes up).
The code that check for collision should go outside of this if block. In fact, this block of code is executed only if you're still pressing space -that is: still firing-. Should collision be checked only if you're "still firing"? Do the fact that you fired a bullet and started waiting for it to destroy the invader interfere in some way with the fact that this bullet can reach the invader and, indeed, destroy it? Of course not!
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
You want collision to be checked in an outside loop, the same that probably also contains the checks for key presses. In this way, even if you're just looking at the screen and waiting, the program keeps testing the condition and, when it's fulfilled, code associated with the event of collision is executed (that is: an invader is "inactivated").
You say //Space , is that what it is or should it be 32 (if ASCII) instead of 57? Does the program flow into the if==57 block?
Your code looks fine, but you need two loops around the collision checker: one for checking all invaders (not just one of them) and another one to check at every bullet position along its trajectory, not just the moment when it leaves the gun.
I will assume we have an auxiliary function that moves the bullet and returns whether it is still inside the screen:
bool BulletIsInScreen();
Then we can write the loops:
if (code == 57) { // Space
while (BulletIsInScreen()) {
for (size_t i = 0; i < counter; ++i) { // counter is the number of invaders,
// according to your comment to your own answer
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[i].MyBullet.Xbpos, Invaders[i].MyBullet.Ybpos,
Invaders[i].MyBullet.BulWidth, Invaders[i].MyBullet.BulHeight,
Invaders[i].Xipos, Invaders[i].Yipos,
Invaders[i].InvWidth, Invaders[i].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[i].Active = false;
printf("Collide!\n");
}
}
}
}
Now this should work as expected.

C++ Qt window positioning

Does Qt has something to offer for the positioning of the tooltip-like windows? (or any types of windows/widgets actually).
I want to be able to update the position of the window automatically, so that it always stays on the screen (or at least fits it as much as possible).
An example of the behaviour I want can be seen in the standard Windows tooltips in the notification area. If the tooltip is big and it has some part of it going off the screen, it gets automatically repositioned.
Obviously, I can write the code myself, but I'm looking for something that has already been written.
I don't know if Qt has one single function that ensures a widget is totally inside of the screen. But with QDesktopWidget it's probably trivial to do.
void function RestrainWidgetToScreen(QWidget * w)
{
QRect screenRect = QDesktopWidget::availableGeometry(w);
if(w->frameGeometry().left() < screenRect.left()) {
w->move(screenRect.left() - w->frameGeometry().left(), 0);
} else if(w->frameGeometry().right() > screenRect.right()) {
w->move(screenRect.right() - w->frameGeometry().right(), 0);
}
if(w->frameGeometry().top() < screenRect.top()) {
w->move(0, screenRect.top() - w->frameGeometry().top());
} else if(w->frameGeometry().bottom() < screenRect.bottom()) {
w->move(0, screenRect.bottom() - w->frameGeometry().bottom());
}
}