1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Battle animations use tick() for updates

This commit is contained in:
Ivan Savenko 2023-05-13 23:38:57 +03:00
parent fa496628f2
commit 8a31aeb94b
14 changed files with 123 additions and 75 deletions

View File

@ -222,7 +222,7 @@ bool DummyAnimation::init()
return true;
}
void DummyAnimation::nextFrame()
void DummyAnimation::tick(uint32_t msPassed)
{
counter++;
if(counter > howMany)
@ -300,7 +300,7 @@ ECreatureAnimType MeleeAttackAnimation::selectGroup(bool multiAttack)
return mutPosToGroup[mutPos];
}
void MeleeAttackAnimation::nextFrame()
void MeleeAttackAnimation::tick(uint32_t msPassed)
{
size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame();
size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
@ -308,7 +308,7 @@ void MeleeAttackAnimation::nextFrame()
if ( currentFrame * 2 >= totalFrames )
owner.executeAnimationStage(EAnimationEvents::HIT);
AttackAnimation::nextFrame();
AttackAnimation::tick(msPassed);
}
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack)
@ -379,15 +379,15 @@ bool MovementAnimation::init()
return true;
}
void MovementAnimation::nextFrame()
void MovementAnimation::tick(uint32_t msPassed)
{
progress += float(GH.getFrameDeltaMilliseconds()) / 1000 * progressPerSecond;
progress += float(msPassed) / 1000 * progressPerSecond;
//moving instructions
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress );
BattleAnimation::nextFrame();
BattleAnimation::tick(msPassed);
if(progress >= 1.0)
{
@ -577,9 +577,9 @@ bool ColorTransformAnimation::init()
return true;
}
void ColorTransformAnimation::nextFrame()
void ColorTransformAnimation::tick(uint32_t msPassed)
{
float elapsed = GH.getFrameDeltaMilliseconds() / 1000.f;
float elapsed = msPassed / 1000.f;
float fullTime = AnimationControls::getFadeInDuration();
float delta = elapsed / fullTime;
totalProgress += delta;
@ -699,7 +699,7 @@ void RangedAttackAnimation::emitProjectile()
projectileEmitted = true;
}
void RangedAttackAnimation::nextFrame()
void RangedAttackAnimation::tick(uint32_t msPassed)
{
// animation should be paused if there is an active projectile
if (projectileEmitted)
@ -716,7 +716,7 @@ void RangedAttackAnimation::nextFrame()
else
stackAnimation(attackingStack)->playUntil(static_cast<size_t>(-1));
AttackAnimation::nextFrame();
AttackAnimation::tick(msPassed);
if (!projectileEmitted)
{
@ -790,9 +790,9 @@ CatapultAnimation::CatapultAnimation(BattleInterface & owner, const CStack * att
logAnim->debug("Created shooting anim for %s", stack->getName());
}
void CatapultAnimation::nextFrame()
void CatapultAnimation::tick(uint32_t msPassed)
{
ShootingAnimation::nextFrame();
ShootingAnimation::tick(msPassed);
if ( explosionEmitted)
return;
@ -988,9 +988,9 @@ bool EffectAnimation::init()
return true;
}
void EffectAnimation::nextFrame()
void EffectAnimation::tick(uint32_t msPassed)
{
playEffect();
playEffect(msPassed);
if (effectFinished)
{
@ -1020,7 +1020,7 @@ void EffectAnimation::onEffectFinished()
effectFinished = true;
}
void EffectAnimation::playEffect()
void EffectAnimation::playEffect(uint32_t msPassed)
{
if ( effectFinished )
return;
@ -1029,7 +1029,7 @@ void EffectAnimation::playEffect()
{
if(elem.effectID == ID)
{
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.getFrameDeltaMilliseconds() / 1000;
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
if(elem.currentFrame >= elem.animation->size())
{
@ -1113,7 +1113,7 @@ void HeroCastAnimation::emitAnimationEvent()
owner.executeAnimationStage(EAnimationEvents::HIT);
}
void HeroCastAnimation::nextFrame()
void HeroCastAnimation::tick(uint32_t msPassed)
{
float frame = hero->getFrame();

View File

@ -48,7 +48,7 @@ public:
bool isInitialized();
bool tryInitialize();
virtual void nextFrame() {} //call every new frame
virtual void tick(uint32_t msPassed) {} //call every new frame
virtual ~BattleAnimation();
BattleAnimation(BattleInterface & owner);
@ -120,7 +120,7 @@ class ColorTransformAnimation : public BattleStackAnimation
float totalProgress;
bool init() override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
public:
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
@ -157,7 +157,7 @@ private:
public:
bool init() override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
~MovementAnimation();
@ -220,7 +220,7 @@ class MeleeAttackAnimation : public AttackAnimation
public:
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
void nextFrame() override;
void tick(uint32_t msPassed) override;
};
@ -246,7 +246,7 @@ public:
~RangedAttackAnimation();
bool init() override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
};
/// Shooting attack
@ -275,7 +275,7 @@ public:
CatapultAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender, int _catapultDmg = 0);
void createProjectile(const Point & from, const Point & dest) const override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
};
class CastAnimation : public RangedAttackAnimation
@ -300,7 +300,7 @@ private:
int howMany;
public:
bool init() override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
DummyAnimation(BattleInterface & owner, int howManyFrames);
};
@ -324,7 +324,7 @@ class EffectAnimation : public BattleAnimation
void onEffectFinished();
void clearEffect();
void playEffect();
void playEffect(uint32_t msPassed);
public:
enum EEffectFlags
@ -349,7 +349,7 @@ public:
~EffectAnimation();
bool init() override;
void nextFrame() override;
void tick(uint32_t msPassed) override;
};
class HeroCastAnimation : public BattleAnimation
@ -367,6 +367,6 @@ class HeroCastAnimation : public BattleAnimation
public:
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
void nextFrame() override;
void tick(uint32_t msPassed) override;
bool init() override;
};

View File

@ -68,7 +68,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height()));
updateAccessibleHexes();
addUsedEvents(LCLICK | RCLICK | MOVE);
addUsedEvents(LCLICK | RCLICK | MOVE | TIME);
}
void BattleFieldController::activate()
@ -134,7 +134,7 @@ void BattleFieldController::renderBattlefield(Canvas & canvas)
renderer.execute(clippedCanvas);
owner.projectilesController->showProjectiles(clippedCanvas);
owner.projectilesController->render(clippedCanvas);
}
void BattleFieldController::showBackground(Canvas & canvas)
@ -611,12 +611,16 @@ void BattleFieldController::showAll(SDL_Surface * to)
show(to);
}
void BattleFieldController::show(SDL_Surface * to)
void BattleFieldController::tick(uint32_t msPassed)
{
updateAccessibleHexes();
owner.stacksController->update();
owner.obstacleController->update();
owner.stacksController->tick(msPassed);
owner.obstacleController->tick(msPassed);
owner.projectilesController->tick(msPassed);
}
void BattleFieldController::show(SDL_Surface * to)
{
Canvas canvas(to);
CSDL_Ext::CClipRectGuard guard(to, pos);

View File

@ -70,6 +70,7 @@ class BattleFieldController : public CIntObject
void showAll(SDL_Surface * to) override;
void show(SDL_Surface * to) override;
void tick(uint32_t msPassed) override;
public:
BattleFieldController(BattleInterface & owner);

View File

@ -202,6 +202,25 @@ const CGHeroInstance * BattleHero::instance()
return hero;
}
void BattleHero::tick(uint32_t msPassed)
{
size_t groupIndex = static_cast<size_t>(phase);
float timePassed = msPassed / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
if(currentFrame >= animation->size(groupIndex))
{
currentFrame -= animation->size(groupIndex);
switchToNextPhase();
}
}
void BattleHero::render(Canvas & canvas)
{
size_t groupIndex = static_cast<size_t>(phase);
@ -219,20 +238,6 @@ void BattleHero::render(Canvas & canvas)
canvas.draw(flagFrame, flagPosition);
canvas.draw(heroFrame, heroPosition);
float timePassed = float(GH.getFrameDeltaMilliseconds()) / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
if(currentFrame >= animation->size(groupIndex))
{
currentFrame -= animation->size(groupIndex);
switchToNextPhase();
}
}
void BattleHero::pause()
@ -354,6 +359,8 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
switchToNextPhase();
play();
addUsedEvents(TIME);
}
HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)

View File

@ -114,6 +114,7 @@ public:
void setPhase(EHeroAnimType newPhase); //sets phase of hero animation
void collectRenderableObjects(BattleRenderer & renderer);
void tick(uint32_t msPassed) override;
float getFrame() const;
void onPhaseFinished(const std::function<void()> &);

View File

@ -159,9 +159,9 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere
}
}
void BattleObstacleController::update()
void BattleObstacleController::tick(uint32_t msPassed)
{
timePassed += GH.getFrameDeltaMilliseconds() / 1000.f;
timePassed += msPassed / 1000.f;
}
std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)

View File

@ -50,7 +50,7 @@ public:
BattleObstacleController(BattleInterface & owner);
/// called every frame
void update();
void tick(uint32_t msPassed);
/// call-in from network pack, add newly placed obstacles with any required animations
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & oi);

View File

@ -58,15 +58,18 @@ void ProjectileMissile::show(Canvas & canvas)
canvas.draw(image, pos);
}
}
float timePassed = GH.getFrameDeltaMilliseconds() / 1000.f;
void ProjectileMissile::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
void ProjectileAnimatedMissile::show(Canvas & canvas)
void ProjectileAnimatedMissile::tick(uint32_t msPassed)
{
ProjectileMissile::show(canvas);
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.getFrameDeltaMilliseconds() / 1000;
ProjectileMissile::tick(msPassed);
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
size_t animationSize = animation->size(reverse ? 1 : 0);
while (frameProgress > animationSize)
frameProgress -= animationSize;
@ -74,9 +77,15 @@ void ProjectileAnimatedMissile::show(Canvas & canvas)
frameNum = std::floor(frameProgress);
}
void ProjectileCatapult::tick(uint32_t msPassed)
{
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
void ProjectileCatapult::show(Canvas & canvas)
{
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.getFrameDeltaMilliseconds() / 1000;
int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0);
@ -90,9 +99,6 @@ void ProjectileCatapult::show(Canvas & canvas)
canvas.draw(image, pos);
}
float timePassed = GH.getFrameDeltaMilliseconds() / 1000.f;
progress += timePassed * speed;
}
void ProjectileRay::show(Canvas & canvas)
@ -135,8 +141,11 @@ void ProjectileRay::show(Canvas & canvas)
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
}
}
}
float timePassed = GH.getFrameDeltaMilliseconds() / 1000.f;
void ProjectileRay::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
@ -217,13 +226,22 @@ void BattleProjectileController::emitStackProjectile(const CStack * stack)
}
}
void BattleProjectileController::showProjectiles(Canvas & canvas)
void BattleProjectileController::render(Canvas & canvas)
{
for ( auto projectile: projectiles)
{
if ( projectile->playing )
projectile->show(canvas);
}
}
void BattleProjectileController::tick(uint32_t msPassed)
{
for ( auto projectile: projectiles)
{
if ( projectile->playing )
projectile->tick(msPassed);
}
vstd::erase_if(projectiles, [&](const std::shared_ptr<ProjectileBase> & projectile){
return projectile->progress > 1.0f;

View File

@ -28,6 +28,7 @@ struct ProjectileBase
{
virtual ~ProjectileBase() = default;
virtual void show(Canvas & canvas) = 0;
virtual void tick(uint32_t msPassed) = 0;
Point from; // initial position on the screen
Point dest; // target position on the screen
@ -42,6 +43,7 @@ struct ProjectileBase
struct ProjectileMissile : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation;
int frameNum; // frame to display from projectile animation
@ -51,7 +53,7 @@ struct ProjectileMissile : ProjectileBase
/// Projectile for spell - render animation moving in straight line from origin to destination
struct ProjectileAnimatedMissile : ProjectileMissile
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
float frameProgress;
};
@ -59,6 +61,7 @@ struct ProjectileAnimatedMissile : ProjectileMissile
struct ProjectileCatapult : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation;
float frameProgress;
@ -68,6 +71,7 @@ struct ProjectileCatapult : ProjectileBase
struct ProjectileRay : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
};
@ -102,7 +106,10 @@ public:
BattleProjectileController(BattleInterface & owner);
/// renders all currently active projectiles
void showProjectiles(Canvas & canvas);
void render(Canvas & canvas);
/// updates positioning / animations of all projectiles
void tick(uint32_t msPassed);
/// returns true if stack has projectile that is yet to hit target
bool hasActiveProjectile(const CStack * stack, bool emittedOnly) const;

View File

@ -335,13 +335,12 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
}
stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
stackAnimation[stack->unitId()]->incrementFrame(float(GH.getFrameDeltaMilliseconds()) / 1000);
}
void BattleStacksController::update()
void BattleStacksController::tick(uint32_t msPassed)
{
updateHoveredStacks();
updateBattleAnimations();
updateBattleAnimations(msPassed);
}
void BattleStacksController::initializeBattleAnimations()
@ -352,21 +351,30 @@ void BattleStacksController::initializeBattleAnimations()
elem->tryInitialize();
}
void BattleStacksController::stepFrameBattleAnimations()
void BattleStacksController::tickFrameBattleAnimations(uint32_t msPassed)
{
for (auto stack : owner.curInt->cb->battleGetAllStacks(false))
{
if (stackAnimation.find(stack->unitId()) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
continue;
stackAnimation[stack->unitId()]->incrementFrame(msPassed / 1000.f);
}
// operate on copy - to prevent potential iterator invalidation due to push_back's
// FIXME? : remove remaining calls to addNewAnim from BattleAnimation::nextFrame (only Catapult explosion at the time of writing)
auto copiedVector = currentAnimations;
for (auto & elem : copiedVector)
if (elem && elem->isInitialized())
elem->nextFrame();
elem->tick(msPassed);
}
void BattleStacksController::updateBattleAnimations()
void BattleStacksController::updateBattleAnimations(uint32_t msPassed)
{
bool hadAnimations = !currentAnimations.empty();
initializeBattleAnimations();
stepFrameBattleAnimations();
tickFrameBattleAnimations(msPassed);
vstd::erase(currentAnimations, nullptr);
if (hadAnimations && currentAnimations.empty())

View File

@ -91,9 +91,9 @@ class BattleStacksController
void removeExpiredColorFilters();
void initializeBattleAnimations();
void stepFrameBattleAnimations();
void tickFrameBattleAnimations(uint32_t msPassed);
void updateBattleAnimations();
void updateBattleAnimations(uint32_t msPassed);
void updateHoveredStacks();
std::vector<const CStack *> selectHoveredStacks();
@ -138,7 +138,7 @@ public:
const CStack* getActiveStack() const;
const std::vector<uint32_t> getHoveredStacksUnitIds() const;
void update();
void tick(uint32_t msPassed);
/// returns position of animation needed to place stack in specific hex
Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;

View File

@ -194,7 +194,7 @@ void CGuiHandler::totalRedraw()
void CGuiHandler::updateTime()
{
int ms = framerateManagerInstance->getElapsedMilliseconds();
int ms = framerateManager().getElapsedMilliseconds();
std::list<CIntObject*> hlp = timeinterested;
for (auto & elem : hlp)
{
@ -693,7 +693,7 @@ void CGuiHandler::renderFrame()
disposed.clear();
}
framerateManagerInstance->framerateDelay(); // holds a constant FPS
framerateManager().framerateDelay(); // holds a constant FPS
}
CGuiHandler::CGuiHandler()
@ -793,7 +793,7 @@ void CGuiHandler::drawFPSCounter()
static SDL_Rect overlay = { 0, 0, 64, 32};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
std::string fps = std::to_string(framerateManagerInstance->getFramerate());
std::string fps = std::to_string(framerateManager().getFramerate());
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
}

View File

@ -87,6 +87,8 @@ private:
void fakeMoveCursor(float dx, float dy);
void fakeMouseButtonEventRelativeMode(bool down, bool right);
FramerateManager & framerateManager();
public:
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
void handleElementDeActivate(CIntObject * elem, ui16 activityFlag);
@ -98,7 +100,7 @@ public:
const Point & getCursorPosition() const;
ShortcutHandler & shortcutsHandler();
FramerateManager & framerateManager();
/// returns duration of last frame in milliseconds
/// NOTE: avoid to use, preferred method is to overload CIntObject::tick(uint32_t)