This commit is contained in:
lunaticbum 2025-08-28 18:23:59 +09:00
parent af80e71d17
commit 167c433a59
9 changed files with 268 additions and 250 deletions

View File

@ -1,5 +1,236 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#include "AnimationStrategy.h" #include "AnimationStrategy.h"
#include "Renderer.h" // Renderer의 그리기 헬퍼 함수들을 사용하기 위해 포함
#include <algorithm> // for std::max, std::clamp
#include <vector>
#include <numeric>
#include <random>
// ====================================================================
// --- PAN (왕복 이동) 애니메이션 ---
// ====================================================================
class PanAnimation : public AnimationStrategy {
public:
PanAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
offsetX_ = 0.0f;
offsetY_ = 0.0f;
cycleComplete_ = false;
xDirection_ = 1;
yDirection_ = 1;
}
bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override {
// 1. 애니메이션이 이미 끝났으면 더 이상 계산하지 않음
if (cycleComplete_) {
// 마지막 위치에 고정하여 그림
renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, getBaseScale(media, surfaceWidth, surfaceHeight));
return cycleComplete_;
}
// 2. Pan 모드에 필요한 overflow(화면 밖으로 넘친 영역) 계산
float overflowX = 0.0f, overflowY = 0.0f;
float scale = getBaseScale(media, surfaceWidth, surfaceHeight);
float mediaW = static_cast<float>(media.getWidth());
float mediaH = static_cast<float>(media.getHeight());
if ((mediaW / mediaH) > ((float)surfaceWidth / surfaceHeight)) {
overflowX = std::max(0.0f, mediaW * scale - surfaceWidth);
} else {
overflowY = std::max(0.0f, mediaH * scale - surfaceHeight);
}
// 3. 좌표 업데이트
bool xDone = (overflowX <= 0);
bool yDone = (overflowY <= 0);
if (overflowX > 0) {
offsetX_ += animationSpeed_ * xDirection_;
if (xDirection_ == 1 && offsetX_ >= overflowX) { offsetX_ = overflowX; xDirection_ = -1; }
else if (xDirection_ == -1 && offsetX_ <= 0) { offsetX_ = 0; xDirection_ = 1; xDone = true; }
}
if (overflowY > 0) {
offsetY_ += animationSpeed_ * yDirection_;
if (yDirection_ == 1 && offsetY_ >= overflowY) { offsetY_ = overflowY; yDirection_ = -1; }
else if (yDirection_ == -1 && offsetY_ <= 0) { offsetY_ = 0; yDirection_ = 1; yDone = true; }
}
// 4. X, Y축 왕복이 모두 끝났는지 확인
if (xDone && yDone) {
cycleComplete_ = true;
}
// 5. 계산된 최종 좌표로 그림
renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, scale);
return cycleComplete_;
}
private:
float offsetX_, offsetY_;
int xDirection_, yDirection_;
bool cycleComplete_;
float getBaseScale(MediaAsset& media, int surfaceWidth, int surfaceHeight) {
float scale;
if ((static_cast<float>(media.getWidth()) / media.getHeight()) > ((float)surfaceWidth / surfaceHeight)) {
scale = (float)surfaceHeight / media.getHeight();
} else {
scale = (float)surfaceWidth / media.getWidth();
}
return scale;
}
};
// ====================================================================
// --- PAN_ONE_WAY (편도 이동) 애니메이션 ---
// ====================================================================
class PanOneWayAnimation : public AnimationStrategy {
public:
PanOneWayAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
offsetX_ = 0.0f;
offsetY_ = 0.0f;
cycleComplete_ = false;
}
bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override {
if (cycleComplete_) {
renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, getBaseScale(media, surfaceWidth, surfaceHeight));
return cycleComplete_;
}
float overflowX = 0.0f, overflowY = 0.0f;
float scale = getBaseScale(media, surfaceWidth, surfaceHeight);
float mediaW = static_cast<float>(media.getWidth());
float mediaH = static_cast<float>(media.getHeight());
if ((mediaW / mediaH) > ((float)surfaceWidth / surfaceHeight)) {
overflowX = std::max(0.0f, mediaW * scale - surfaceWidth);
} else {
overflowY = std::max(0.0f, mediaH * scale - surfaceHeight);
}
bool xReachedEnd = (overflowX <= 0);
bool yReachedEnd = (overflowY <= 0);
if (overflowX > 0) {
offsetX_ += animationSpeed_;
if (offsetX_ >= overflowX) { offsetX_ = overflowX; xReachedEnd = true; }
}
if (overflowY > 0) {
offsetY_ += animationSpeed_;
if (offsetY_ >= overflowY) { offsetY_ = overflowY; yReachedEnd = true; }
}
if (xReachedEnd && yReachedEnd) {
cycleComplete_ = true;
}
renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, scale);
return cycleComplete_;
}
private:
float offsetX_, offsetY_;
bool cycleComplete_;
float getBaseScale(MediaAsset& media, int surfaceWidth, int surfaceHeight) {
// (PanAnimation과 중복되지만, 각 클래스의 독립성을 위해 포함)
float scale;
if ((static_cast<float>(media.getWidth()) / media.getHeight()) > ((float)surfaceWidth / surfaceHeight)) {
scale = (float)surfaceHeight / media.getHeight();
} else {
scale = (float)surfaceWidth / media.getWidth();
}
return scale;
}
};
// ====================================================================
// --- ZOOM 애니메이션 ---
// ====================================================================
class ZoomAnimation : public AnimationStrategy {
public:
ZoomAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
scaleMultiplier_ = 1.0f;
cycleComplete_ = false;
zoomDirection_ = 1;
}
bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override {
if (!cycleComplete_) {
scaleMultiplier_ += 0.0005f * animationSpeed_ * zoomDirection_;
if (zoomDirection_ == 1 && scaleMultiplier_ >= 1.2f) { scaleMultiplier_ = 1.2f; zoomDirection_ = -1; }
else if (zoomDirection_ == -1 && scaleMultiplier_ <= 1.0f) { scaleMultiplier_ = 1.0f; zoomDirection_ = 1; cycleComplete_ = true; }
}
// ZOOM은 중앙 정렬을 기본으로 함
float baseScale, baseOffsetX, baseOffsetY;
renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY);
renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale * scaleMultiplier_);
return cycleComplete_;
}
private:
float scaleMultiplier_;
int zoomDirection_;
bool cycleComplete_;
};
// ====================================================================
// --- PAGE_TURN (대기) 애니메이션 ---
// ====================================================================
class PageTurnAnimation : public AnimationStrategy {
public:
PageTurnAnimation(float speed, long long delay) : AnimationStrategy(speed), delayMs_(delay) { reset(); }
void reset() override {
cycleComplete_ = false;
startTime_ = std::chrono::steady_clock::now();
}
bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override {
if (!cycleComplete_) {
long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - startTime_).count();
if (elapsed >= delayMs_) {
cycleComplete_ = true;
}
}
// 대기하는 동안 중앙에 고정된 이미지를 그림
float baseScale, baseOffsetX, baseOffsetY;
renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY);
renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale);
return cycleComplete_;
}
private:
long long delayMs_;
std::chrono::steady_clock::time_point startTime_;
bool cycleComplete_;
};
// ====================================================================
// --- NONE (애니메이션 없음) ---
// ====================================================================
class NoneAnimation : public AnimationStrategy {
public:
NoneAnimation(float speed) : AnimationStrategy(speed) {}
void reset() override {} // 아무것도 안 함
bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override {
// 중앙에 고정된 이미지만 그림
float baseScale, baseOffsetX, baseOffsetY;
renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY);
renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale);
return true; // 즉시 완료
}
};

View File

@ -1,30 +1,22 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#pragma once #pragma once
#include <chrono> #include <android/native_window.h>
#include "MediaAsset.h"
// 애니메이션의 현재 상태를 담을 구조체 class Renderer; // 전방 선언
struct AnimationState {
float offsetX = 0.0f;
float offsetY = 0.0f;
float scale = 1.0f;
bool cycleComplete = false;
};
// 모든 '전문 요리사'의 기반이 될 추상 클래스
class AnimationStrategy { class AnimationStrategy {
public: public:
virtual ~AnimationStrategy() = default; virtual ~AnimationStrategy() = default;
// 매 프레임마다 호출되어 애니메이션 상태를 업데이트하고 반환 /**
virtual AnimationState update(float overflowX, float overflowY) = 0; * @brief .
* @return true
*/
virtual bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) = 0;
// 애니메이션 상태를 처음으로 리셋
virtual void reset() = 0; virtual void reset() = 0;
protected: protected:
// 생성자에서 애니메이션 속도를 받아 저장
AnimationStrategy(float speed) : animationSpeed_(speed) {} AnimationStrategy(float speed) : animationSpeed_(speed) {}
float animationSpeed_; float animationSpeed_;
}; };

View File

@ -1,17 +0,0 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#include "AnimationStrategy.h"
#include <algorithm> // for std::max
#ifndef LUNARLAUNCHER_NONEANIMATION_H
#define LUNARLAUNCHER_NONEANIMATION_H
class NoneAnimation : public AnimationStrategy {
public:
NoneAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override { state_.cycleComplete = true; }
AnimationState update(float, float) override { return state_; }
private:
AnimationState state_;
};
#endif //LUNARLAUNCHER_NONEANIMATION_H

View File

@ -1,35 +0,0 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#include "AnimationStrategy.h"
#include <algorithm> // for std::max
#ifndef LUNARLAUNCHER_PAGETURNANIMATION_H
#define LUNARLAUNCHER_PAGETURNANIMATION_H
// --- PAGE_TURN (대기 후 페이드) 애니메이션 ---
class PageTurnAnimation : public AnimationStrategy {
public:
PageTurnAnimation(float speed, long long delay) : AnimationStrategy(speed), delayMs_(delay) { reset(); }
void reset() override {
state_.cycleComplete = false;
startTime_ = std::chrono::steady_clock::now();
}
AnimationState update(float, float) override {
if (state_.cycleComplete) return state_;
long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - startTime_).count();
if (elapsed >= delayMs_) {
state_.cycleComplete = true;
}
return state_;
}
private:
AnimationState state_;
long long delayMs_;
std::chrono::steady_clock::time_point startTime_;
};
#endif //LUNARLAUNCHER_PAGETURNANIMATION_H

View File

@ -1,48 +0,0 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#ifndef LUNARLAUNCHER_PANANIMATION_H
#define LUNARLAUNCHER_PANANIMATION_H
#include "AnimationStrategy.h"
#include <algorithm> // for std::max
// --- PAN (왕복) 애니메이션 ---
class PanAnimation : public AnimationStrategy {
public:
PanAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
state_.offsetX = 0.0f;
state_.offsetY = 0.0f;
state_.cycleComplete = false;
xDirection_ = 1;
yDirection_ = 1;
}
AnimationState update(float overflowX, float overflowY) override {
if (state_.cycleComplete) return state_;
bool xDone = (overflowX <= 0);
bool yDone = (overflowY <= 0);
if (overflowX > 0) {
state_.offsetX += animationSpeed_ * xDirection_;
if (xDirection_ == 1 && state_.offsetX >= overflowX) { state_.offsetX = overflowX; xDirection_ = -1; }
else if (xDirection_ == -1 && state_.offsetX <= 0) { state_.offsetX = 0; xDirection_ = 1; xDone = true; }
}
if (overflowY > 0) {
state_.offsetY += animationSpeed_ * yDirection_;
if (yDirection_ == 1 && state_.offsetY >= overflowY) { state_.offsetY = overflowY; yDirection_ = -1; }
else if (yDirection_ == -1 && state_.offsetY <= 0) { state_.offsetY = 0; yDirection_ = 1; yDone = true; }
}
if (xDone && yDone) state_.cycleComplete = true;
return state_;
}
private:
AnimationState state_;
int xDirection_, yDirection_;
};
#endif //LUNARLAUNCHER_PANANIMATION_H

View File

@ -1,53 +0,0 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#include "AnimationStrategy.h"
#include <algorithm> // for std::max
#ifndef LUNARLAUNCHER_PANONEWAYANIMATION_H
#define LUNARLAUNCHER_PANONEWAYANIMATION_H
// --- ⬇️ 새로운 PAN_ONE_WAY (편도) 애니메이션 클래스 추가 ⬇️ ---
class PanOneWayAnimation : public AnimationStrategy {
public:
PanOneWayAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
state_.offsetX = 0.0f;
state_.offsetY = 0.0f;
state_.cycleComplete = false;
}
AnimationState update(float overflowX, float overflowY) override {
if (state_.cycleComplete) return state_;
bool xReachedEnd = (overflowX <= 0);
bool yReachedEnd = (overflowY <= 0);
if (overflowX > 0) {
state_.offsetX += animationSpeed_; // 항상 정방향(+)으로만 이동
if (state_.offsetX >= overflowX) {
state_.offsetX = overflowX; // 끝에 도달하면 멈춤
xReachedEnd = true;
}
}
if (overflowY > 0) {
state_.offsetY += animationSpeed_; // 항상 정방향(+)으로만 이동
if (state_.offsetY >= overflowY) {
state_.offsetY = overflowY; // 끝에 도달하면 멈춤
yReachedEnd = true;
}
}
// X축과 Y축 이동이 모두 끝났다면 사이클 완료
if (xReachedEnd && yReachedEnd) {
state_.cycleComplete = true;
}
return state_;
}
private:
AnimationState state_;
};
#endif //LUNARLAUNCHER_PANONEWAYANIMATION_H

View File

@ -1,10 +1,6 @@
#include "Renderer.h" #include "Renderer.h"
#include "AnimationStrategy.cpp" #include "AnimationStrategy.cpp"
#include "TransitionStrategy.cpp" #include "TransitionStrategy.cpp"
#include "NoneAnimation.h"
#include "PageTurnAnimation.h"
#include "PanAnimation.h"
#include "PanOneWayAnimation.h"
#include <android/log.h> #include <android/log.h>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -107,62 +103,47 @@ void Renderer::determineActiveAnimationMode() {
// ==================================================================== // ====================================================================
void Renderer::handleAnimationState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight) { void Renderer::handleAnimationState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight) {
AnimationState animState; // 안전장치: 애니메이션 전문가가 고용되지 않았다면 작업을 중단
if (animationStrategy_) { if (!animationStrategy_) {
float overflowX = 0.0f, overflowY = 0.0f; // 이 경우엔 그냥 검은 화면만 그림
if (activeAnimationMode_ == AnimationMode::PAN || activeAnimationMode_ == AnimationMode::PAN_ONE_WAY) {
float mediaW = static_cast<float>(currentMedia_.getWidth());
float mediaH = static_cast<float>(currentMedia_.getHeight());
if ((mediaW / mediaH) > (static_cast<float>(surfaceWidth) / surfaceHeight)) {
float scale = static_cast<float>(surfaceHeight) / mediaH;
overflowX = std::max(0.0f, mediaW * scale - surfaceWidth);
} else {
float scale = static_cast<float>(surfaceWidth) / mediaW;
overflowY = std::max(0.0f, mediaH * scale - surfaceHeight);
}
}
animState = animationStrategy_->update(overflowX, overflowY);
}
memset(buffer.bits, 0, buffer.stride * buffer.height * sizeof(uint32_t)); memset(buffer.bits, 0, buffer.stride * buffer.height * sizeof(uint32_t));
float finalOffsetX, finalOffsetY, finalScale; return;
if (activeAnimationMode_ == AnimationMode::PAN || activeAnimationMode_ == AnimationMode::PAN_ONE_WAY) {
float scale;
if ((static_cast<float>(currentMedia_.getWidth()) / currentMedia_.getHeight()) > (static_cast<float>(surfaceWidth) / surfaceHeight)) {
scale = static_cast<float>(surfaceHeight) / currentMedia_.getHeight();
} else {
scale = static_cast<float>(surfaceWidth) / currentMedia_.getWidth();
} }
finalScale = scale * animState.scale;
finalOffsetX = animState.offsetX;
finalOffsetY = animState.offsetY;
} else {
float baseScale, baseOffsetX, baseOffsetY;
calculateFitScaleAndOffset(currentMedia_, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY);
finalScale = baseScale * animState.scale;
finalOffsetX = baseOffsetX + animState.offsetX;
finalOffsetY = baseOffsetY + animState.offsetY;
}
drawMedia(buffer, currentMedia_, 1.0f, finalOffsetX, finalOffsetY, finalScale);
if (animState.cycleComplete && nextMedia_.isValid()) { // 1. 위임: 현재 애니메이션 전문가에게 모든 계산과 그리기를 맡기고, 완료 여부만 보고받음
// (이 execute 함수 내부에서 배경을 지우고, 좌표를 계산하고, drawMedia를 호출하는 모든 작업을 수행함)
bool isCycleComplete = animationStrategy_->execute(this, buffer, currentMedia_, surfaceWidth, surfaceHeight);
// 2. 상태 전환: 애니메이션이 끝났고, 다음 미디어가 대기 중이라면 '전환' 상태로 넘어감
if (isCycleComplete && nextMedia_.isValid()) {
currentState_ = RenderState::TRANSITIONING; currentState_ = RenderState::TRANSITIONING;
transitionStartTime_ = std::chrono::steady_clock::now(); transitionStartTime_ = std::chrono::steady_clock::now();
// 사용자가 설정한 전환 모드를 확인
TransitionMode transModeToUse = configuredTransitionMode_; TransitionMode transModeToUse = configuredTransitionMode_;
if (transModeToUse == TransitionMode::RANDOM) { if (transModeToUse == TransitionMode::RANDOM) {
// RANDOM일 경우, FADE, SLIDE, MOSAIC 중에서 무작위로 선택
std::uniform_int_distribution<int> dist(0, 2); std::uniform_int_distribution<int> dist(0, 2);
transModeToUse = static_cast<TransitionMode>(dist(randomEngine_)); int randomChoice = dist(randomEngine_);
if (randomChoice == 0) transModeToUse = TransitionMode::FADE;
else if (randomChoice == 1) transModeToUse = TransitionMode::SLIDE;
else transModeToUse = TransitionMode::MOSAIC;
} }
// 결정된 모드에 맞는 '전환 전문가' 객체를 생성
if (transModeToUse == TransitionMode::SLIDE) { if (transModeToUse == TransitionMode::SLIDE) {
transitionStrategy_ = std::make_unique<SlideTransition>(fadeDurationMs_, surfaceWidth); transitionStrategy_ = std::make_unique<SlideTransition>(fadeDurationMs_, surfaceWidth);
} else if (transModeToUse == TransitionMode::MOSAIC) { } else if (transModeToUse == TransitionMode::MOSAIC) {
transitionStrategy_ = std::make_unique<MosaicTransition>(fadeDurationMs_, 20, 32, randomEngine_); transitionStrategy_ = std::make_unique<MosaicTransition>(fadeDurationMs_, 20, 32, randomEngine_);
} else { } else { // 기본값은 FADE
transitionStrategy_ = std::make_unique<FadeTransition>(fadeDurationMs_); transitionStrategy_ = std::make_unique<FadeTransition>(fadeDurationMs_);
} }
// 새로운 전환 전문가의 상태를 초기화
if(transitionStrategy_) transitionStrategy_->reset(); if(transitionStrategy_) transitionStrategy_->reset();
LOGI("Animation complete. Switching to TRANSITIONING state.");
} }
} }
@ -175,7 +156,6 @@ void Renderer::handleTransitionState(ANativeWindow_Buffer& buffer, int surfaceWi
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - transitionStartTime_).count(); long long elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - transitionStartTime_).count();
// 1. 상태 보고: isComplete()를 호출해서 끝났는지 "물어보기만" 함
bool isComplete = transitionStrategy_->isComplete(elapsed); bool isComplete = transitionStrategy_->isComplete(elapsed);
transitionStrategy_->execute(this, buffer, currentMedia_, nextMedia_, elapsed); transitionStrategy_->execute(this, buffer, currentMedia_, nextMedia_, elapsed);
@ -183,6 +163,7 @@ void Renderer::handleTransitionState(ANativeWindow_Buffer& buffer, int surfaceWi
if (isComplete) { if (isComplete) {
currentMedia_ = std::move(nextMedia_); currentMedia_ = std::move(nextMedia_);
currentState_ = RenderState::ANIMATING; currentState_ = RenderState::ANIMATING;
animationCycleComplete_ = false; // 새 미디어의 애니메이션 시작 준비
determineActiveAnimationMode(); determineActiveAnimationMode();
if(animationStrategy_) animationStrategy_->reset(); if(animationStrategy_) animationStrategy_->reset();
} }

View File

@ -63,7 +63,7 @@ private:
TRANSITIONING TRANSITIONING
}; };
RenderState currentState_ = RenderState::ANIMATING; RenderState currentState_ = RenderState::ANIMATING;
bool animationCycleComplete_ = false; // <-- 이 플래그가 다시 필요
// --- 상태별 처리를 위한 private 핸들러 함수 --- // --- 상태별 처리를 위한 private 핸들러 함수 ---
void handleAnimationState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight); void handleAnimationState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight);
void handleTransitionState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight); void handleTransitionState(ANativeWindow_Buffer& buffer, int surfaceWidth, int surfaceHeight);

View File

@ -1,33 +0,0 @@
//
// Created by JIBUM HAN on 2025. 8. 28..
//
#include "AnimationStrategy.h"
#include <algorithm> // for std::max
#ifndef LUNARLAUNCHER_ZOOMANIMATION_H
#define LUNARLAUNCHER_ZOOMANIMATION_H
// --- ZOOM 애니메이션 ---
class ZoomAnimation : public AnimationStrategy {
public:
ZoomAnimation(float speed) : AnimationStrategy(speed) { reset(); }
void reset() override {
state_.scale = 1.0f;
state_.cycleComplete = false;
zoomDirection_ = 1;
}
AnimationState update(float, float) override {
if (state_.cycleComplete) return state_;
state_.scale += 0.0005f * animationSpeed_ * zoomDirection_;
if (zoomDirection_ == 1 && state_.scale >= 1.2f) { state_.scale = 1.2f; zoomDirection_ = -1; }
else if (zoomDirection_ == -1 && state_.scale <= 1.0f) { state_.scale = 1.0f; zoomDirection_ = 1; state_.cycleComplete = true; }
return state_;
}
private:
AnimationState state_;
int zoomDirection_;
};
#endif //LUNARLAUNCHER_ZOOMANIMATION_H