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.
Related
For this project i use OpenGl.
I'm trying to detect the collision between two spheres, I think it's pretty simple.
It works but sometimes (very often actually...) the collision between these two spheres is not detected, and I really don't know why...
I tried to change the algorithm of detection and it allways ends by a non detection sometimes...
void MyGlWindow::checkMoverIsHit()
{
for (size_t i = 0; i < m_container->m_movers.size(); i++)
{
if (m_container->m_ball != m_container->m_movers[i]) {
float size = m_container->m_ball->m_size + m_container->m_movers[i]->m_size;
if (size >= (m_container->m_ball->m_position - m_container->m_movers[i]->m_position).magnitude())
{
score += 10;
m_container->m_movers.erase(std::remove(m_container->m_movers.begin(), m_container->m_movers.end(), m_container->m_movers[i]), m_container->m_movers.end());
for (size_t i = 0; i < (score / 10) + 1; i++)
createMover();
reload();
}
}
}
}
I call this function in my update function
void MyGlWindow::update()
{
TimingData::get().update();
if (!run) return;
float lastFrameDuration = (float)TimingData::get().lastFrameDuration;
float duration = lastFrameDuration * 0.003;
totalTimePrecise += lastFrameDuration;
totalTimeSec = totalTimePrecise / 1000;
if (totalTimeSec > 60) {
writeBestScoreInFile();
restart();
}
if (totalTimeSec == 40)
windBlowing = true;
if (duration <= 0.0f) return;
m_container->update(duration);
if (windBlowing == true)
m_container->windBlow();
checkBallDetachFromAnchor();
checkMoverIsHit(); // !!! ITS CALLED HERE !!!
m_world->runPhysics(duration);
}
m_container is my container of movers: it contains basically all the "movers" of my scene.
m_container->m_ball is a pointer of one of these movers. It's the ball I launched to "shoot" the other movers.
After I think everything is pretty clear, ask me if something is not.
Hope someone can help me... I've done everything in my project it's the only thing bugging !
Here is a GIF, the first ball worked, the second didn't:
I finally manage to find out what was happening.
The problem is that my world is avoiding collisions. What I mean by that is: when two objects collide, they automatically bounce against each other.
This behaviour was the problem because the goal of my hunting ball is to collide against the other objects so.... randomly... it was not detecting some collisions, because it was boucing.
To fix it, I removed from my hunting ball the fact that it was iteracting with the other movers of the scene. So now it's only checking in my function checkMoverIsHit(); if it's colliding with the other movers.
It now works perfectly.
So for a University Project I am looking to try and change the tint of a sprite over a specific time frame. The aim is to make a set of rocks turn from grey to orange (simulating heating them up) over 5 seconds, then turn them from orange to grey again (simulating cooling them down) over the next 5 seconds.
This is done using the DirectXTK, SpriteBatch specifically, however I appear to be having some problems controlling the heating up and cooling down logic. Currently, the rocks heat up to orange, but do not cool back down.
The Update function for the rocks, along with a further HeatDelay function I am using to control the cooling down are are included below.
timeToChangeColour is initialised at 5.
void RockFade::Update(float timeDelta)
{
if ((timeDelta >= timeToChangeColour) && (heatDelay == false))
{
this->heatUp = false;
HeatDelay();
timeToChangeColour = timeDelta + 10;
}
else if ((timeDelta < timeToChangeColour) && (heatDelay == false))
{
this->heatUp = true;
}
if (heatUp)
{
this->newBlue -= 0.002f;
}
else
{
this->newBlue += 0.002f;
}
this->spriteTint = DirectX::SimpleMath::Color{ 1.0f, 1.0f, newBlue, 1.0f};
}
void RockFade::HeatDelay()
{
heatDelay = true;
Sleep(5);
heatDelay = false;
}
Any help is greatly appreciated!
The biggest conceptual problem with your code is that in the function
void RockFade::Update(float timeDelta);
You seem to assume that timeDelta will be a time difference since a specified fixed time in the past, but it is actually the time difference since the last time Update was called.
Because of this the condition
timeDelta < timeToChangeColour
seem to be always true (because it seems timeToChangeColour's value exceeds one frame time).
The first step towards achieving what you are trying to do here might be to accumulate the time, and use it as your basis, e.g.:
this->myAccumulatedTime += timeDelta; // initialize it to zero
How can I set up an if statement so after every frame the sprite shows the next frame and then the next and then once it goes through all the frames it is over?
I tried using if statements and it has never worked for me, could anyone give an example?
Edit:
After demand for code I have decided to add a sample.
int frame4 = 1;
if(frame4 = 1)
{
WalkDownFrame1(); //Renders frame 4
}
else if(frame4 = 2)
{
WalkDownFrame2(); //Renders frame 2
}
else if(frame4 = 3)
{
WalkDownFrame3(); //Renders frame 3
}
else if(frame4 = 4)
{
WalkDownFrame4(); //Renders frame 4
}
else if(frame4 = 5)
{
frame4 = 1;
}
frame4++;
no matter what modifications I apply it stays stuck on one frame.
I'm assuming you mean if the conditions are true the animation occurs and if the conditions are false it stops, in which case it would look like
/*Rest of your model transformation code*/
if(shouldbeanimating){
/*animation code*/
}else{
/*default state or nothing if you want the model to
freeze at that point in the animation*/
}
Then whenever the program should stop the animation you just set shouldbeanimating to false
Well, you need to know how many frames your animation has. Then you proceed to draw frame after frame. If you hit the last frame you go back to the first or you stop.
Here's a link that will help you. It's doesnt matter if its SDL or any other lib, the approach is always the same.
http://lazyfoo.net/SDL_tutorials/lesson20/index.php
As an example
while(isAnimating)
{
framecounter++;
isAnimating = (framecounter < MAX_FRAMES);
}
They're many solutions. You can do a Sprite Class and add an attribute _tick, store your Texture into a container, like a std::vector. I'll give you a short hint. You put a method tick() to increment your tick number. You put an attribute nbFrames, that contains the number of frames for the current sprite.
int tick;
int nbFrames; //equivalent of textures.size()
std::vector<SDL_Texture*> textures;
SDL_Texture *idle_frame;
bool moving;
int x;
int y;
Sprite::Sprite()
{
_tick = 0;
//init your textures
moving = false;
x = 0;
y = 0;
}
int _tick;
void Sprite::tick(void)
{
// Consider that _tick can reach the Max Value of int32
_tick++;
}
void Sprite::render(void)
{
SDL_Texture *texture;
if(moving)
{
int fr_index = _tick % nbFrames;
texture = textures[fr_index];
}
else
{
texture = idle_frame;
}
//then you do your render code with SDL_RenderCopy, or OpenGL code
//.
//..
}
Still missing some other thing to handle, but that an hint for your solution.
Is it possible that you are just assigning in the if statement and not testing? Or did you just miss typed it here?
Because if your code is if(frame = 0) it should be (frame == 0).
Im aware this code just looks like a massive block of mess so iv'e done my best to comment what i can... If anyone regularly does skeletal animation im hoping youl see whats going on here.
The problem im having is that float interp = (next-current)/(next-start); is not returning whats expected, e.g. values greater than 1 and minus values... Im guessing this is the whole reason the animations arent being shown and theres no other underlying mistakes.. If there is anything obvious sticking out please let me know.
m_animations stores all the keyframe information for a joint.
void Animation::getRotation(Joint& J) {
int i=0,j=0; //i for bone to animation j for which keyframe in animation
float start, next, current = m_time.elapsed(); //storing time elapsed to keep it the same thoughout method to avoid errors
for (i=0; i<m_animations.size(); i++) {
if(m_animations[i].m_bname == J.name) //finds which bone is being animated
break;
} //retrieve the correct 'anim' for Joint
if (current > m_animations[i].rx_time[m_animations[i].rx_time.size()-1]) { //checks to see if end of animation
m_time.restart();
current = m_time.elapsed(); //resets the animation at its end
}
for (j=0; j<m_animations[i].rx_time.size()-1; j++) {
if(m_animations[i].rx_time[j] >= next && next < m_animations[i].rx_time[j+1]) { //finds the keyframe
start = m_animations[i].rx_time[j]; //start time of current frame
next = m_animations[i].rx_time[j+1]; //end time of current frame
break;
}
}
cout << start <<" "<< current <<" "<< m_time.elapsed() <<" "<< next << endl;
//Get start and end quaternions for slerp
Rotation3 Rj(m_animations[i].rx_angle[j], m_animations[i].ry_angle[j], m_animations[i].rz_angle[j], J.translation);
J.quat = Rj.GetQuat(); //rotating to
Rotation3 R = Rotation3(m_animations[i].rx_angle[j+1], m_animations[i].ry_angle[j+1], m_animations[i].rz_angle[j+1], J.translation);
Quat4 q = R.GetQuat(); //rotating from
float interp = (next-current)/(next-start); //find interpolation point
Quat4 slerp = Slerp(J.quat, q, interp); //sphereical linear interpolation
R = Rotation3(slerp,J.translation);
J.rotation.PasteRotation(R.GetRotationMatrix());
}
Also if it helps heres the update skeleton function that calls getRotation
void Animation::update_skeleton(Joint& J) {
getRotation(J);
J.world.PasteTranslation(J.translation); //world becomes translation matrix
J.world *= J.rotation;
if(J.pName != "") {
J.world = Mat4(J.parent->world) *= J.world; //as not to overwrite the parents world matrix
}
J.translation = J.world.ExtractTranslation();
for(int i=0; i<J.children.size(); i++) {
update_skeleton(*J.children[i]);
}
}
Also when i run my program, it seems to be as if there is only one joint... So im guessing something might be going wrong with the J.translation value during getRotation, but im hoping that fixing my interpolation problem might solve this...
Any help will be VERY appreciated.
You seem to be resetting current before using the original values of start and next, if I've read the code properly that will be causing current to be greater than next, leading to yor negative interpolations.
Turns out there were quite a few mistakes... But the main error was to do with pointers, I fixed it by giving the bones ids for parents, children and itself. Then I changed the functions to take bone id instead of reference to a joint. Problem = fixed :D
I am trying to create a 2D platformer (Mario-type) game and I am some having some issues with handling collisions properly. I am writing this game in C++, using SDL for input, image loading, font loading, etcetera. I am also using OpenGL via the FreeGLUT library in conjunction with SDL to display graphics.
My method of collision detection is AABB (Axis-Aligned Bounding Box), which is really all I need to start with. What I need is an easy way to both detect which side the collision occurred on and handle the collisions properly. So, basically, if the player collides with the top of the platform, reposition him to the top; if there is a collision to the sides, reposition the player back to the side of the object; if there is a collision to the bottom, reposition the player under the platform.
I have tried many different ways of doing this, such as trying to find the penetration depth and repositioning the player backwards by the penetration depth. Sadly, nothing I've tried seems to work correctly. Player movement ends up being very glitchy and repositions the player when I don't want it to. Part of the reason is probably because I feel like this is something so simple but I'm over-thinking it.
If anyone thinks they can help, please take a look at the code below and help me try to improve on this if you can. I would like to refrain from using a library to handle this (as I want to learn on my own) or the something like the SAT (Separating Axis Theorem) if at all possible. Thank you in advance for your help!
void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
{
de2dObj ballPrev;
ballPrev.coords[0] = ball.coords[0];
ballPrev.coords[1] = ball.coords[1];
ballPrev.coords[2] = ball.coords[2];
ballPrev.coords[3] = ball.coords[3];
ballPrev.coords[0] -= ball.xspeed;
ballPrev.coords[1] -= ball.yspeed;
ballPrev.coords[2] -= ball.xspeed;
ballPrev.coords[3] -= ball.yspeed;
int up = 0;
int left = 0;
int right = 0;
int down = 0;
if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
{
left = 1;
}
if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
{
right = 1;
}
if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
{
up = 1;
}
if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
{
down = 1;
}
cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;
if (left == 1)
{
ball.coords[0] = block[i].coords[0] - 18.0f;
ball.coords[2] = block[i].coords[0] - 2.0f;
}
else if (right == 1)
{
ball.coords[0] = block[i].coords[2] + 2.0f;
ball.coords[2] = block[i].coords[2] + 18.0f;
}
else if (down == 1)
{
ball.coords[1] = block[i].coords[3] + 4.0f;
ball.coords[3] = block[i].coords[3] + 20.0f;
}
else if (up == 1)
{
ball.yspeed = 0.0f;
ball.gravity = 0.0f;
ball.coords[1] = block[i].coords[1] - 17.0f;
ball.coords[3] = block[i].coords[1] - 1.0f;
}
}
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
{
ball.gravity = -0.5f;
}
}
}
To explain what some of this code means:
The blocks variable is basically an integer that is storing the amount of blocks, or platforms. I am checking all of the blocks using a for loop, and the number that the loop is currently on is represented by integer i.
The coordinate system might seem a little weird, so that's worth explaining.
coords[0] represents the x position (left) of the object (where it starts on the x axis).
coords[1] represents the y position (top) of the object (where it starts on the y axis).
coords[2] represents the width of the object plus coords[0] (right).
coords[3] represents the height of the object plus coords[1] (bottom).
de2dCheckCollision performs an AABB collision detection.
Up is negative y and down is positive y, as it is in most games.
Hopefully I have provided enough information for someone to help me successfully. If there is something I left out that might be crucial, let me know and I'll provide the necessary information. Finally, for anyone who can help, providing code would be very helpful and much appreciated.
Thank you again for your help!
Edit 2: I have updated my code with a new algorithm that checks where the ball was previously before collision. Corner cases work on that single platform correctly now, and when I have a wall of objects, I can slide against it correctly now. The only remaining problem is that there is a small jittering effect that happens when I am on the ground, where the ball is constantly going up and down as if it is being pulled by gravity and then the ball falls back into the object again.
Edit: Here is a URL to an image trying to show the kinds of problems I am having:
http://img8.imageshack.us/img8/4603/collisionproblem.png
In case the explanation in the picture doesn't make too much sense, the ball cannot move left past the corner of an object unless I jump over it. However, the ball can move right, but it gets repositioned to the right of the object while moving, which is not needed. This creates a skipping movement essentially, where it appears as the the ball is skipping over half of the object or so when I move right. If this doesn't make sense, please ask me and I'll try to clarify more.
One problem with your code is that you only detect situations like this:
If the circle happens to be fully inside the block, you don't reposition at all. And that's a problem.
You're trying to think about your simulation as if it were continuous, but keep in mind it's discrete. In general, if you only look at the current state of the ball, you really cannot know which side it collided with. Look at these two possibilities:
The first solution that comes to mind is to look at the last position of the ball as well; more precisely, look at the delta vector. See if the delta vector intersects a wall. If it does, reposition in an axis-aligned direction towards the wall intersected by the delta vector.
Edit: When I said "delta vector", I forgot that you're moving a square and not a single point. So, if you just look at the delta vector of the top-left corner, that's not going to be enough because it may not detect that part of the ball entered a block. Instead, you can look at the delta vectors of all 4 corners.