c++ Qt QGraphicsItemAnimation model of thrown ball - c++

I have problem with the animation of a ball, which flies according to the equations of motion
x = speed*cos(angle) * time;
y = speed*sin(angle) * time - (g*pow(time,2)) / 2;
I create a QGraphicsScene with QGraphicsEllipseItem
QGraphicsScenescene = new QGraphicsScene;
QGraphicsEllipseItemball = new QGraphicsEllipseItem(0,scene);
then I try to animate ball
scene->setSceneRect( 0.0, 0.0, 640.0, 480.0 );
ball->setRect(15,450,2*RADIUS,2*RADIUS);
setScene(scene);
QTimeLine *timer = new QTimeLine(5000);
timer->setFrameRange(0, 100);
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation;
animation->setItem(ball);
animation->setTimeLine(timer);
animation->setPosAt(0.1, QPointF(10, -10));
timer->start();
But I can't understand how setPosAt works and how I can use my calculated x,y in this case.
The official Qt documentation for setPosAt is very short and incomprehensible.

You need to call setPosAt() multiple times with various values of (step) between 0.0 and 1.0. Then when you play the animation, Qt will use linear interpolation to animate smoothly between the points you set, as Qt increases its "current step" value from 0.0 to 1.0.
For example, to make the ball move in a straight line you could do something like:
animation->setPosAt(0.0, QPointF(0,0));
animation->setPosAt(1.0, QPointF(10,0));
... or to make the ball go up, and then down, you could do:
animation->setPosAt(0.0, QPointF(0,0));
animation->setPosAt(0.5, QPointF(0,10));
animation->setPosAt(1.0, QPointF(0,0));
... so to get the arc you want you could do something like:
for (qreal step=0.0; step<1.0; step += 0.1)
{
qreal time = step*10.0; // or whatever the relationship should be between step and time
animation->setPosAt(step, QPointF(speed*cos(angle) * time, speed*sin(angle) * time - (g*pow(time,2)) / 2);
}

Related

Qt instantly rotate animation

I know this may be a stupid question but I really can't seem to find an answer anywhere. I created a triangle like this:
QPolygonF triangle;
triangle.append(QPointF(0., -15));
triangle.append(QPointF(30., 0));
triangle.append(QPointF(0., 15));
triangle.append(QPointF(15., 0));
This triangle shall represent a car on my map, and I need to animate it. So i did the following:
QGraphicsItemAnimation *animation;
QGraphicsPolygonItem *clientCar;
QTimeLine *timer;
animation = new QGraphicsItemAnimation;
timer = new QTimeLine(10000);
timer->setFrameRange(0, 100);
clientCar = scene->addPolygon(triangle, myPen, myBrush)
animation->setItem(clientCar);
animation->setTimeLine(10000);
animation->setPosAt(0.f / 200.f, map.street1);
animation->setRotationAt(10.f / 200.f, 90.f);
animation->setPosAt(10.f / 200.f, map.street2);
animation->setRotationAt(20.f / 200.f, 180.f);
animation->setPosAt(20.f / 200.f, map.street3);
scene->addItem(clientCar);
ui->graphicsView->setScene(scene);
timer->start();
The problem is, when it reaches an intersection(road cross) it should rotate so that it will face the road it's going next. As you can see above, I tried using setRotationAt(), but what it does is slowly rotating between intersections until it reaches the next one. It should be turning in an instant, only when it's changing it's way. Any help?
From the doc:
QGraphicsItemAnimation will do a simple linear interpolation between
the nearest adjacent scheduled changes to calculate the matrix. For
instance, if you set the position of an item at values 0.0 and 1.0,
the animation will show the item moving in a straight line between
these positions. The same is true for scaling and rotation.
The linear interpolation part will do the trick.
So why don't you try this:
//animation->setPosAt(0.f / 200.f, map.street1);
//animation->setRotationAt(10.f / 200.f, 90.f);
//animation->setPosAt(10.f / 200.f, map.street2);
//animation->setRotationAt(20.f / 200.f, 180.f);
//animation->setPosAt(20.f / 200.f, map.street3);
static float const eps = 1.f / 200.f;
QVector<float> steps = {0.f, 10.f / 200.f, 20.f / 200.f};
QVector<QPointF> points = {map.street1, map.street2, map.street3};
QVector<float> angles = {0, 90.f, 180.f};
// initial conditions
animation->setPosAt(steps[0], points[0]);
animation->setRotationAt(steps[0], angles[0]);
// for each intersection
for(size_t inters = 1; inters < points.size(); ++inters)
{
animation->setRotationAt(steps[inters] - eps, angles[inters - 1]);
animation->setPosAt(steps[inters], points[inters]);
animation->setRotationAt(steps[inters] + eps, angles[inters]);
}

Crossed physics bodies issue in cocos2dxv3.4

I am new to Cocos2dx v3.x.I am developing a basic car racing game in which the car can move within the continuous scrolling track which is straight and small curvy like "Road Fighter" game in Google play Store.For moving the car,i am using device accelerometer and adjusts the velocity of the car body acc. to that.For the path body,i am using EdgeChain Shape of 5 points which i have shown below so that it makes the bound area for the car motion and car should not go beyond that.But with setVelocity function,my car body goes outside the boundary.I have also set the CollisionBitmask and ContactTestBitmask,collision is detected,even then the physics body of car crossed over the edge chain body of path.
auto *sprite = Sprite::create("Road.jpg");
sprite->setPosition(Vec2(winSize.width/2,winSize.height/2));
sprite->setScaleX(rX);
sprite->setScaleY(rY);
this->addChild(sprite, 0);
auto edge_body=PhysicsBody::createEdgeChain(vec ,5 ,PHYSICSBODY_MATERIAL_DEFAULT,3);
edge_body->setDynamic(false);
edge_body->addShape(PhysicsShapeEdgeChain::create(vec3, 5));
sprite->setPhysicsBody(edge_body);
edge_body->setCollisionBitmask(1);
edge_body->setContactTestBitmask(true);
//where vec and vec3 are vectors of 5 points acc to track boundary.
auto *mycar=Sprite::create(car_png_name);
mycar->setPosition(Point(winSize.width/2,winSize.height*0.35));
addChild(mycar,2);
mycar_body=PhysicsBody::createBox(mycar->getContentSize(),PhysicsMaterial(0,1,0));
mycar->setPhysicsBody(mycar_body);
mycar_body->setRotationEnable(false);
mycar_body->setCollisionBitmask(2);
mycar_body->setContactTestBitmask(true);
// DEVICE ACCELEROMETER
Device::setAccelerometerEnabled(true);
auto listener= EventListenerAcceleration::create (CC_CALLBACK_2(HelloWorld::OnAcceleration,this));
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
auto contact_listener=EventListenerPhysicsContact::create();
contact_listener->onContactBegin=CC_CALLBACK_1(HelloWorld::onContactBegin, this);
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contact_listener, this);
void HelloWorld::OnAcceleration(cocos2d::Acceleration *acc, cocos2d::Event *event)
{
#define KFILTERINGFACTOR 0.7
#define KRESTACCELX 0
#define KsquirrelMAXPOINTSPERSEC (winSize.width*0.5)
#define KMAXDIFFX 0.15
double rollingX=0;
acc->y = acc->x;
rollingX = (acc->y * KFILTERINGFACTOR) + (rollingX * (1.0 - KFILTERINGFACTOR));
float accelX = acc->y - rollingX;
float accelDiff = accelX - KRESTACCELX;
float accelFraction = accelDiff / KMAXDIFFX;
carspeed = KsquirrelMAXPOINTSPERSEC * accelFraction*1.5;
if(mycar_body!=NULL)
mycar_body->setVelocity(Vec2(carspeed*1.5,0));
}
I have faced this issue in Cocos2dxv3.4.The same logic works fine in Cocos2dxv2.2.6 with box2d b2body.I am not getting where i am wrong or how in other way,i can move my car with accelerometer within the scrolling boundary.Can anyone Suggest?
Thanks in advance.

How to rotate the physics or dynamic body along an arc path in cocos2d(box2D)

As I am newbie to cocoa2d I am struggling alot to rotate the physics or dynamic body along an arc path.
The way I tried is as follows:
#define COS_ANIMATOR(position, timeCount, speed, waveMagnitude) ((cosf(timeCount * speed) * waveMagnitude) + position)
#define SIN_ANIMATOR(position, timeCount, speed, waveMagnitude) ((sinf(timeCount * speed) * waveMagnitude) + position)
CCSpriteBatchNode *pipe_parent = [CCSpriteBatchNode batchNodeWithFile:#"pipe.png" capacity:100];
CCTexture2D *pipeSpriteTexture_ = [pipe_parent texture];
PhysicsSprite *pipeSprite = [PhysicsSprite spriteWithTexture:pipeSpriteTexture_ rect:CGRectMake(0 ,0 ,55,122)];
//pipe = [CCSprite spriteWithFile:#"pipe.png"
// rect:CGRectMake(0, 0, 55, 122)];
[self addChild:pipeSprite];
// pipe.position = ccp(s.width/2 , 420.0);
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody; //this will be a dynamic body
myBodyDef.position.Set(((s.width/2) - 90)/PTM_RATIO, 420.0/PTM_RATIO); //set the starting position
myBodyDef.angle = 0; //set the starting angle
b2Body* staticBody = world->CreateBody(&myBodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
boxFixtureDef.userData = pipeSprite;
boxFixtureDef.filter.groupIndex = -1;
staticBody->CreateFixture(&boxFixtureDef);
[pipeSprite setPhysicsBody:staticBody];
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
const CGPoint newSpritePosition = ccp(COS_ANIMATOR(150, mTimeCounter, 0.05,50), SIN_ANIMATOR(400, mTimeCounter, -0.05, 50));
pipeSprite.position = newSpritePosition;
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
on following this approach my sprite rotating in circular motion instead of rotating in an arc path.
Please give your ideas or suggestions.
Thanks all
I'm not entirely sure what it is you are looking to accomplish when you talk about rotating in an arc path. I only see you setting a position, not a rotation, so are you just wanting to set a position, or a rotation, or both? Your position code looks like you are trying to achieve a circular (or elliptical) path because you are using the sine and cosine in the x,y position.
If you're looking to move a sprite along a sine curve, I did that today and it took a bit of trial and error. I had some variables for the amplitude and period, and from there I traced out a nice sine curve movement in the sprite's update: method.
CGPoint initialPosition; // set this to the sprite's initial position
float amplitude; // in points
float period; // in points
float y, x = initialPosition.x;
-(void) update:(ccTime)dt
{
x += dt * 100; // speed of movement across the screen. Picked by trial and error.
y = initalPosition.y + amplitude * sinf((x - initialPosition.x)/period);
sprite.position = ccp(x,y);
sprite.rotation = cosf((x - initialPosition.x)/period); // optional if you want to rotate along the path as well
}
Don't know if this is anything you are looking for but it might give you some ideas.

How to move a sprite at a certain angle with a joystick

Hi I have finally made a working joystick in cocos2d. I am able to rotate a sprite to the exact angle that the joystick thumb, or cap, is 'pointing'. However, I am unable to move the sprite in that same direction. Is there an easy way to move the sprite with the way I have the rotating code set up? Also is there a way to keep it moving if your thumb is still pressed, but not moving the joystick?. PS this code is all within the TouchesMoved method. PPS. the cap is the thumb, the pad is the joystick background, and the Sprite2 is the sprite that I want to move. (95, 95) is the center of the pad sprite.
if(capSprite.position.x>=padSprite.position.x){
id a3 = [CCFlipX actionWithFlipX:NO];
[sprite2 runAction:a3];
}
if(capSprite.position.x<=padSprite.position.x){
id a4 = [CCFlipX actionWithFlipX:YES];
[sprite2 runAction:a4];
}
CGPoint pos1 = ccp(95, 95);
CGPoint pos2 = ccp(capSprite.position.x, capSprite.position.y);
int offX = pos2.x-pos1.x;
int offY = pos2.y-pos1.y;
float angleRadians = atanf((float)offY/(float)offX);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float theAngle = -1 * angleDegrees;
sprite2.rotation = theAngle;
I'm not familiar with cocos2d but I had a quick look at the documentation and this sample might be of use to you:
if keys[key.UP]:
self.target.acceleration = (200 * rotation_x, 200 * rotation_y)
I had written a long explanation answering your second question but I believe this "self.target.acceleration" solves that too. You can read more at the cocos2d API documentation.
What I generally do is get the angle, convert it to a CGPoint with ccpForAngle(float) and then multiply the CGPoint by a value:
float angle = whatever;
CGPoint anglePoint = ccpForAngle(angle);
// You will need to play with the mult value
angle = ccpMult(angle, 2.5);
// This also works with box2D or probably Chipmunk.
sprite.position = angle;

At will scaling and rotation in a 2D space

I'm trying to write a graphical program in C++ with QT where users can scale and rotate objects with the mouse (just like inkscape or CorelDraw does), however after many months trying to make it happen I still cannot make it work. It currently works for example by just rotating or just scaling, but not when the user want to transform the object in an arbitrary way. There is an example in QT about affine transformation but it is very simple (e.g., it scales using a single factor not x and Y factors), it not provides scale directions or fixed scaling point) so I don't know how to extend it or use it.
This is how the program is expected to behave:
The user drop a polygon in the canvas.
If the user clicks on the polygon a set of blue boxes will appear around the object. These boxes are used to scale the object in any direction (e.g., up, down, left, right, etc)
If the user clicks again in the polygon a set of red boxes will appear around the object. These boxes are used to rotate the object in any direction.
So, how can I implement at least the following:
If the user click on the top blue box (scale towards up), hold the left button and moves the mouse toward up, how can I make the polygon to scale towards up? Do I need scale direction? Do I need a general Fixed-point of scaling? How can I calculate the scale factors as the mouse move towards up so the polygon is scaled in "real time"?
Here is the code that in my perspective could make it work: See the code here But it does not work :-( . If you can help me with a better implementation I will appreciate it.
Sorry to put to many questions but I am completely frustrated.
Thanks,
Carlos.
cannot make it work
the result is just wrong
Doesn't describe your problem very well.
Basically I don't know what is needed in terms of the concatenation/multiplications of matrices
In object store:
1. position
2. rotation
3. scale
When you need to draw object, perform operations in this order:
1. Scale using stored scale factor
2. Rotate using stored angle
3. Translate to position
Given scale factor s and rotation angle r, to rotate/scale object (point array, or whatever) around arbitrary point (p.x, p.y), do this:
1. Translate object to -p.x, -p.y . I.e. for every vertex do vertex -= p;
2. Scale object. For every vertex do vertex *= s
3. Rotate object. Rotate every vertex around point zero using angle r.
4. Translate object to p.x, p.y.
Also I'd recommend to take a look at "Affine Transformations" demo in Qt 4. To view demo, launch qtdemo, select "Demonstrations->Affine Transformations".
Consider hiring a geometry tutor. "Months" is too long to deal with rotate/scale/translate problem.
But, I have no clue on how to combine of these function in a proper order
If you're rotating and scaling around same point, the order of operations doesn't matter.
--EDIT--
Live example:
Points indicate pivot, start of transform, and end of transform.
Wireframe letters represent original image.
Red letter represent "rotate and uniformly scale" transform.
Green letters represent "2D scale" transform.
For both transform you need pivot, point where you began to drag shape, and point where you stopped dragging shape.
I will not ever explain this again.
transformtest.pro:
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += MainWindow.h
SOURCES += main.cpp MainWindow.cpp
main.cpp:
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char** argv){
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
MainWindow.h:
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <QGLWidget>
class QPaintEvent;
class MainWindow: public QWidget{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0);
protected slots:
void updateAngle();
protected:
void paintEvent(QPaintEvent* ev);
float angle;
float distAngle;
};
#endif
MainWindow.cpp:
#include "MainWindow.h"
#include <QTimer>
#include <QPainter>
#include <QColor>
#include <QVector2D>
#include <math.h>
static const int timerMsec = 50;
static const float pi = 3.14159265f;
MainWindow::MainWindow(QWidget* parent)
:QWidget(parent), angle(0), distAngle(0){
QTimer* timer = new QTimer(this);
timer->start(timerMsec);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
connect(timer, SIGNAL(timeout()), this, SLOT(updateAngle()));
}
float randFloat(){
return (qrand()&0xFF)/255.0f;
}
float randFloat(float f){
return randFloat()*f;
}
inline QVector2D perp(const QVector2D v){
return QVector2D(-v.y(), v.x());
}
void MainWindow::updateAngle(){
angle = fmod(angle + pi*5.0f/180.0f, pi*2.0f);
distAngle = fmod(distAngle + pi*1.0f/180.0f, pi*2.0f);
}
QTransform buildRotateScale(QVector2D pivot, QVector2D start, QVector2D end){
QVector2D startDiff = start - pivot;
QVector2D endDiff = end - pivot;
float startLength = startDiff.length();
float endLength = endDiff.length();
if (startLength == 0)
return QTransform();
if (endLength == 0)
return QTransform();
float s = endLength/startLength;
startDiff.normalize();
endDiff.normalize();
QVector2D startPerp = perp(startDiff);
float rotationAngle = acos(QVector2D::dotProduct(startDiff, endDiff))*180.0f/pi;
if (QVector2D::dotProduct(startPerp, endDiff) < 0)
rotationAngle = -rotationAngle;
return QTransform().translate(pivot.x(), pivot.y()).rotate(rotationAngle).scale(s, s).translate(-pivot.x(), -pivot.y());
}
QTransform buildScale(QVector2D pivot, QVector2D start, QVector2D end){
QVector2D startDiff = start - pivot;
QVector2D endDiff = end - pivot;
float startLength = startDiff.length();
float endLength = endDiff.length();
if ((startDiff.x() == 0)||(startDiff.y() == 0))
return QTransform();
QVector2D s(endDiff.x()/startDiff.x(), endDiff.y()/startDiff.y());
return QTransform().translate(pivot.x(), pivot.y()).scale(s.x(), s.y()).translate(-pivot.x(), -pivot.y());
}
void MainWindow::paintEvent(QPaintEvent* ev){
QPainter painter(this);
QPointF pivot(width()/2, height()/2);
QPointF transformStart(pivot.x() + 100.0f, pivot.y() - 100.0f);
float r = sinf(distAngle)*100.0f + 150.0f;
QPointF transformEnd(pivot.x() + r*cosf(angle), pivot.y() - r*sinf(angle));
painter.fillRect(this->rect(), QBrush(QColor(Qt::white)));
QPainterPath path;
QString str(tr("This is a test!"));
QFont textFont("Arial", 40);
QFontMetrics metrics(textFont);
QRect rect = metrics.boundingRect(str);
path.addText(QPoint((width()-rect.width())/2, (height()-rect.height())/2), textFont, str);
painter.setPen(QColor(200, 200, 255));
painter.drawPath(path);
painter.setTransform(buildRotateScale(QVector2D(pivot), QVector2D(transformStart), QVector2D(transformEnd)));
painter.fillPath(path, QBrush(QColor(255, 100, 100)));
painter.setPen(QColor(100, 255, 100));
painter.setTransform(buildScale(QVector2D(pivot), QVector2D(transformStart), QVector2D(transformEnd)));
painter.fillPath(path, QBrush(QColor(100, 255, 100)));
painter.setTransform(QTransform());
QPainterPath coords;
r = 10.0f;
coords.addEllipse(pivot, r, r);
coords.addEllipse(transformStart, r, r);
coords.addEllipse(transformEnd, r, r);
painter.setPen(QPen(QBrush(Qt::red), 5.0f));
painter.setBrush(QBrush(QColor(127, 0, 0)));
painter.setPen(QPen(QBrush(Qt::green), 5.0f));
painter.drawLine(QLineF(pivot, transformStart));
painter.setPen(QPen(QBrush(Qt::blue), 5.0f));
painter.drawLine(QLineF(transformStart, transformEnd));
painter.setPen(Qt::red);
painter.drawPath(coords);
painter.end();
}
Basically, you have a point (or series of points) that you want to transform with two linear transformations, R (rotation) and S (scaling). So you're trying to calculate something like
R(S(x))
where x is a point. If you represent these operations using matrices, then performing consecutive operations is equivalent to multiplying the matrices, i.e.
R*S*x
Unfortunately, you haven't given enough information for me to be more specific...could you post some code (just the small, relevant parts) showing what you're doing? What do you mean by "natural way"? What about your result is "just wrong"?