Lets say I have 4 images and I want to use these 4 images to animate a character. The 4 images represent the character walking. I want the animation to repeat itself as long as I press the key to move but to stop right when I unpress it. It doesn't need to be SFML specific if you don't know it, just basic theory would really help me.
Thank you.
You may want some simple kind of state machine. When the key is down (see sf::Input's IsKeyDown method), have the character in the "animated" state. When the key is not down, have the character in "not animated" state. Of course, you could always skip having this "state" and just do what I mention below (depending on exactly what you're doing).
Then, if the character is in the "animated" state, get the next "image" (see the next paragraph for more details on that). For example, if you have your images stored in a simple 4 element array, the next image would be at (currentIndex + 1) % ARRAY_SIZE. Depending on what you are doing, you may want to store your image frames in a more sophisticated data structure. If the character is not in the "animated" state, then you wouldn't do any updating here.
If your "4 images" are within the same image file, you can use the sf::Sprite's SetSubRect method to change the portion of the image displayed. If you actually have 4 different images, then you probably would need to use the sf::Sprite's SetImage method to switch the images out.
How would you enforce a framerate so that the animation doesn't happen too quickly?
Hello please see my answer here and accept this post as the best solution.
https://stackoverflow.com/a/52656103/3624674
You need to supply duration per frame and have the total progress be used to step through to the frame.
In the Animation source file do
class Animation {
std::vector<Frame> frames;
double totalLength;
double totalProgress;
sf::Sprite *target;
public:
Animation(sf::Sprite& target) {
this->target = ⌖
totalProgress = 0.0;
}
void addFrame(Frame& frame) {
frames.push_back(std::move(frame));
totalLength += frame.duration;
}
void update(double elapsed) {
// increase the total progress of the animation
totalProgress += elapsed;
// use this progress as a counter. Final frame at progress <= 0
double progress = totalProgress;
for(auto frame : frames) {
progress -= (*frame).duration;
// When progress is <= 0 or we are on the last frame in the list, stop
if (progress <= 0.0 || &(*frame) == &frames.back())
{
target->setTextureRect((*frame).rect);
break; // we found our frame
}
}
};
To stop when you unpress, simply only animate when the key is held
if(isKeyPressed) {
animation.update(elapsed);
}
To support multiple animations for different situations have a boolean for each state
bool isWalking, isJumping, isAttacking;
...
if(isJumping && !isWalking && !isAttacking) {
jumpAnimation.update(elapsed);
} else if(isWalking && !isAttacking) {
walkAnimation.update(elapsed);
} else if(isAttacking) {
attackAnimation.update(elapsed);
}
...
// now check for keyboard presses
if(jumpkeyPressed) { isJumping = true; } else { isJumping false; }
Related
I am currently experiencing some heavy slowdowns with my game. I have narrowed it down to something related with texture animations.
In my game there are characters that walk in 1 of 4 possible directions, they will walk up to a point, then change direction and continue walking (sort of like a tower defense game).
First i am loading the sprite frame cache like this
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("characters.plist");
This code is only run once during the life time of my application.
When the characters get loaded to the screen their animation is being set using the following code:
int direction = 0;
int number = 0;
if (this->to_x < 0) // Left
{
direction = 1;
number = 1;
}
else if(this->to_x > 0) // Right
{
direction = 2;
number = 1;
}
if (this->to_y < 0) // Down
{
direction = 0;
number = 0;
}
else if(this->to_y > 0) // Up
{
direction = 3;
number = 2;
}
int s = 0; //skin
// Set the animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
string frame_sprite_name = StringUtils::format("%s_%d_%d_%d.png",parameters[name].image_name.c_str(),s,number,i);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);
if (frame) {
animation->addSpriteFrame(frame);
} else {
break;
}
}
// Invert the sprite when they go right
if (direction == 2) {
setFlippedX(true);
}else{
setFlippedX(false);
}
// Set the pace of the animation based on the type
if (name=="runner") {
animation->setDelayPerUnit(0.15f);
} else{
animation->setDelayPerUnit(0.3f);
}
Animate *animate = Animate::create(animation);
this->stopAllActions();
this->runAction(RepeatForever::create(animate));
What this code does is:
Check the direction
Get the sprite frame from the cache based on the direction
Run the action with repeat forever.
However this code is ran every time they change direction to set the new animation of the active characters. Also, at one time I can have around 40-50 of these characters going around.
I've noticed that after a few minutes in the game the slowdown starts to happen as soon as a new "character" is created, (since they are created in rapid succession in waves). And the slowdown also happens when the characters change in direction. So this makes me believe I am using the textures wrong.
If anyone knows how to fix this please let me know.
PD: I was thinking about the possibility of pre-loading all the animations and then just having each of the sprites that represent the characters run the corresponding animation.
You should definitely cache the animation in the AnimationCache with addAnimation and getAnimation methods.
I am having some troubles while designing a C++ QT application. Among other things, the application displays the beams (or rays) of some lasers (each laser is composed by around 700 segments starting from the same position and spaced by a constant angle, and a small circle at the other end of the segment materializing the end point). Here is a drafting of one laser I found on the internet . Most of the time I have to display 12 lasers (so 12*700 segments), at 30 frames per second. My first implementation works, but it takes a huge amount of CPU (>12%) and the GUI lags a lot.
What I did for the implementation is quite simple: I have a LaserModel class, filled by a thread receiving TCP beacons; A MainWindow class with a QGraphicsScene* _scene; and a LaserView class, whose instances are added to the scene. A signal at 30fps triggers the passage of the datas from the model to the view using the setData() method.
I did a performance analysis and it seems that 80% of the work of the gui (There is way more than just displaying these lasers) is done by the drawLine(/*…*/) methods.
I am sure that there is a more elegant and efficient way to do that.
First the setData(/*…*/) method seems quite ugly to me. I know that I should probably emit a signal (like laserHasChanged(const LaserModel&)) from the model and catch it in the view (with a slot onNewData(const LaserModel&)) but for the performance point of view it seems worse (but it looks better for the coupling point of view, especially because in the future I will probably also display a property tree with the configuration of the lasers(so another view of the same model)).
More important I am not quite sure about what the drawLine(const QLine& line) does, but this part of the code should just “render” the line, not recreate it.
So the question is: how can I drastically improve the performances of the rendering? Especially the drawline part of the paint() method?
Here is the relevant code:
class LaserView : public QGraphicsItem {
/* c-tor, d-tor, members... */
std::vector<Beam> _beams;
QRectF _boundingRect;
void setData(const Sim::Pose& pose, const std::vector<Beam>& beams, const QRectF& boundingRect, const bool& showMaxRanges) {
/*...*/
_beams = beams;
_boundingRect = boundingRect;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) {
painter->setPen(_color);
// Display only the beams that are stopped by an obstacle
if (!_showMaxRanges) {
for (unsigned int i = 0 ; i < _beams.size() -1 ; i++) {
if (!_beams[i].endPointType == laserBeamEndPointType::MAX_RANGE) {
_beams[i]._line.setLine(_pose.x, _pose.y, _beams[i]._mapEndPose.x(), _beams[i]._mapEndPose.y());
painter->drawLine(_beams[i]._line);
}
}
}
// Display all beams
else {
for (unsigned int i = 0 ; i < _beams.size() -1 ; i++) {
_beams[i]._line.setLine(_pose.x, _pose.y, _beams[i]._mapEndPose.x(), _beams[i]._mapEndPose.y());
painter->drawLine(_beams[i]._line);
}
}
// Draw a small circle to visualize easily the end points of the beams when they stop against an obstacle
for (unsigned int i = 0; i < _beams.size() - 1 ; i++) {
// Draw the end point in blue if the obstacle is reflective
if (_beams[i].endPointType == laserBeamEndPointType::REFLECTIVE_OBJECT) {
painter->setBrush(REFLETIVE_END_POINT_COLOR);
painter->drawEllipse(_beams[i]._mapEndPose, END_POINT_SIZE, END_POINT_SIZE);
}
// Draw the end point in red if the obstacle is NOT reflective
else if (_beams[i].endPointType == laserBeamEndPointType::NOT_REFLECTIVE_OBJECT) {
painter->setBrush(NOT_REFLETIVE_END_POINT_COLOR);
painter->drawEllipse(_beams[i]._mapEndPose, END_POINT_SIZE, END_POINT_SIZE);
}
}
}
}
You are drawing one line at a time, one ellipse at a time, switching between brushes in the process. There is a significant overhead with these operations (at least with Qt4). You can do better by aggregating these calls to just 3 drawing calls.
you have
std::vector<QlineF> lines;
std::vector<QRectF> ellipse_refl_rects;
std::vector<QRectF> ellipse_non_refl_rects;
Rather than drawing a line add to lines collection. Same for ellipses.
Then at the end you have
if(!lines.isempty())
{
painter->drawLines(&lines[0], lines.size());
}
if(!ellipse_refl_rects.isempty())
{
painter->setBrush(REFLETIVE_END_POINT_COLOR);
painter->drawEllipses(&ellipse_refl_rects[0], ellipse_refl_rects.size());
}
if(!ellipse_non_refl_rects.isempty())
{
painter->setBrush(NOT_REFLETIVE_END_POINT_COLOR);
painter->drawEllipses(&ellipse_non_refl_rects[0], ellipse_non_refl_rects.size());
}
I have several videos about 5 minutes each along with annotation data containing information about each object's bounding box coordinates at each frame number.
I am trying to read the videos and draw lines between center of bounding boxes frame by frame (when the current frame number matches the number from the ground truth data). I don't want to do this in a batch process, but every 30 or 60 frames would work for me.
Here is my code:
VideoCapture capture(path_video);
if (!capture.isOpened()){
cout << "Failed to capture frame/Open the file" << "\n";
return 0;
}
bool stop(false);
while(!stop){
capture >> frame;
if (frame.data==NULL) {
break;
}
double rate = capture.get(CV_CAP_PROP_FPS);
int delay = 1000/rate;
frmNum = (capture.get(CV_CAP_PROP_POS_FRAMES));
for (int i=0 ; i<db.size() ; i++){//db is a vector of vector that has annotation data for each object splited in inner vectors , and is sorted ascendingly on start frame number of objects.
if (!db[i].empty()){
for (int j=1 ; j<db[i].size() ; j++){
if(frmNum == db[i][j-1].currFrame){
cv::line(frame, db[i][j-1].pnt, db[i][j].pnt,Scalar(255,0,0),2);
}
else{
break;
}
}
}
}
imshow("Video", frame);
int key = waitKey(delay);
if (key==27){
break;
}
I checked and my if condition becomes true but no line is drawn on the video. I guess I don't see the lines because frames are changing and the drawn lines are cleared by new frames, But I couldnt come up with an alternative way. Thanks for your help.
If you want to draw the annotations on every frame and you don't mind that the information may be "obsolete" in some cases, I would do the following:
have a global image variable called "overlay" which would hold the most up to date (in regards to current frame number) representation of annotations and would have the same size as a single frame from the videostream
maintain an array of indices called "last_object_annotation_idx", which would store for each object the last index of its annotation already seen/used (initially set to -1 for each object)
in each iteration of the main loop, update the "last_object_annotation_idx" array (that is, for each object check if the current frame number matches the "currFrame" field of the next annotation - I am using the information that the array of annotations is sorted)
if there was a change to the array, redraw the overlay image using annotations referenced from "last_object_annotation_idx"
finally, add the overlay image to the frame and display the result.
Also, in the if statement
if(frmNum == db[i][j-1].currFrame){
cv::line(frame, db[i][j-1].pnt, db[i][j].pnt,Scalar(255,0,0),2);
}
else{
break;
}
isn't the "break" kind of wrong? It means you will break out of the loop if the first check fails, so you will not see anything past the first index in that case.
I am attempting to insert a delay in Processing sketch. I tried Thread.sleep() but I guess it will not work because, as in Java, it prevents rendering of the drawings.
Basically, I have to draw a triangle with delays in drawing three sides.
How do I do that?
Processing programs can read the value of computer’s clock. The current second is read with the second() function, which returns values from 0 to 59. The current minute is read with the minute() function, which also returns values from 0 to 59. - Processing: A Programming Handbook
Other clock related functions : millis(), day(), month(), year().
Those numbers can be used to trigger events and calculate the passage of time, as in the following Processing sketch quoted from the aforementioned book:
// Uses millis() to start a line in motion three seconds
// after the program starts
int x = 0;
void setup() {
size(100, 100);
}
void draw() {
if (millis() > 3000) {
x++;
line(x, 0, x, 100);
}
}
Here's an example of a triangle whose sides are drawn each one after 3 seconds (the triangle is reset every minute):
int i = second();
void draw () {
background(255);
beginShape();
if (second()-i>=3) {
vertex(50,0);
vertex(99,99);
}
if (second()-i>=6) vertex(0,99);
if (second()-i>=9) vertex(50,0);
endShape();
}
As #user2468700 suggests, use a time keeping function. I like millis().
If you have a value to keep track of the time at certain intervals and the current time (continuously updated) you can check if one timer(manually updated one) falls behind the other(continuous one) based on a delay/wait value. If it does, update your data (number of points to draw in this case) and finally the local stop-watch like value.
Here's a basic commented example.
Rendering is separated from data updates to make it easier to understand.
//render related
PVector[] points = new PVector[]{new PVector(10,10),//a list of points
new PVector(90,10),
new PVector(90,90)};
int pointsToDraw = 0;//the number of points to draw on the screen
//time keeping related
int now;//keeps track of time only when we update, not continuously
int wait = 1000;//a delay value to check against
void setup(){
now = millis();//update the 'stop-watch'
}
void draw(){
//update
if(millis()-now >= wait){//if the difference between the last 'stop-watch' update and the current time in millis is greater than the wait time
if(pointsToDraw < points.length) pointsToDraw++;//if there are points to render, increment that
now = millis();//update the 'stop-watch'
}
//render
background(255);
beginShape();
for(int i = 0 ; i < pointsToDraw; i++) {
vertex(points[i].x,points[i].y);
}
endShape(CLOSE);
}
I have a program in which I am drawing images on the screen. The draw function here is called per frame inside in which I have all my drawing code.
I have written an image sequencer that return the respective image from an index of images.
void draw()
{
sequence.getFrameForTime(getCurrentElapsedTime()).draw(0,0); //get current time returns time in float and startson application start
}
On key press, I have start the sequences from the first image [0] and then go on further. So, everytime I press a key, it has to start from [0] unlike the above code where it basically uses the currentTime%numImages to get the frame (which is not the start 0 position of image).
I was thinking to write a timer of own that basically can be triggered everytime I press the key so that the time always starts from 0. But before doing that, I wanted to ask if anybody had better/easier implementation ideas for this?
EDIT
Why I didn't use just a counter?
I have framerate adjustments in my ImageSequence as well.
Image getFrameAtPercent(float rate)
{
float totalTime = sequence.size() / frameRate;
float percent = time / totalTime;
return setFrameAtPercent(percent);
}
int getFrameIndexAtPercent(float percent){
if (percent < 0.0 || percent > 1.0) percent -= floor(percent);
return MIN((int)(percent*sequence.size()), sequence.size()-1);
}
void draw()
{
sequence.getFrameForTime(counter++).draw(0,0);
}
void OnKeyPress(){ counter = 0; }
Is there a reason this wont suffice?
What you should do is increase a "currentFrame" as a float and convert it to an int to index your frame:
void draw()
{
currentFrame += deltaTime * framesPerSecond; // delta time being the time between the current frame and your last frame
if(currentFrame >= numImages)
currentFrame -= numImages;
sequence.getFrameAt((int)currentFrame).draw(0,0);
}
void OnKeyPress() { currentFrame = 0; }
This should gracefully handle machines with different framerates and even changes of framerates on a single machine.
Also, you won't be skipping part of a frame when you loop over as the remainder of the substraction is kept.