> 文章列表 > Sweet Snippet 之 PlayMode实现

Sweet Snippet 之 PlayMode实现

Sweet Snippet 之 PlayMode实现

本文简述了一种 PlayMode 的实现方式

PlayMode(播放模式)应该是编程开发中常见的概念,一般来讲有以下几种播放模式:

enum class EPlayMode : uint8
{Once, // 单次播放Loop, // 循环播放PingPong, // 往返播放
};

总结来讲, PlayMode 影响的是 时间因子(TimeFactor) 的计算,我们可以抽象出以下结构来进行实现说明:

class CPlayMode
{
public:CPlayMode(EPlayMode Mode_, float Duration_) :Mode(Mode_),Duration(Duration_),Elapsed(0){}void Update(float DeltaTime);float GetFactor() const;protected:    EPlayMode Mode;float Duration;float Elapsed;
};

开始实现之前,我们先定义几个辅助函数:

float FloatDivide(float Dividend, float Divisor)
{if (Divisor != 0){return Dividend / Divisor;}return 0;
}float FloatModulo(float Dividend, float Divisor)
{if (Divisor != 0){return Dividend - ((int)(Dividend / Divisor)) * Divisor;}return 0;
}

作用就是处理浮点数的除法和取模(用以解决除零等问题).

之后便是正式的实现了:

void CPlayMode::Update(float DeltaTime)
{switch (Mode){case EPlayMode::Once:{Elapsed += DeltaTime;if (Elapsed > Duration){Elapsed = Duration;}}break;case EPlayMode::Loop:{Elapsed += DeltaTime;if (Elapsed > Duration){Elapsed = FloatModulo(Elapsed, Duration);}}break;case EPlayMode::PingPong:{Elapsed += DeltaTime;float LoopDuration = 2.0f * Duration;if (Elapsed > LoopDuration){Elapsed = FloatModulo(Elapsed, LoopDuration);}}break;}
}

Update 函数主要用于更新 Elapsed:

  • 对于 Once 模式,直接使用 Duration 截断 Elapsed
  • 对于 Loop 模式,简单处理的话可以直接 Elapsed - Duration(当 Elapsed 超过 Duration 时),但是使用 FloatModulo 会更健壮一些(可以处理 DeltaTime 过大的情况)
  • 对于 PingPong 模式,处理上会取巧一些,直接将往返一次算作一次循环,这样处理上就和 Loop 模式类似了(原本的 Duration 需要加倍)

GetFactor 就是按照 Elapsed 获取当前的 TimeFactor :

float CPlayMode::GetFactor() const
{switch (Mode){case EPlayMode::Once:case EPlayMode::Loop:{return FloatDivide(Elapsed, Duration);}case EPlayMode::PingPong:{if (Elapsed <= Duration){return FloatDivide(Elapsed, Duration);}else{return 1.0f - FloatDivide(Elapsed - Duration, Duration);}}}return 0;
}

其中 PingPong 模式的处理会有些特殊,但是了解了上面 Elapsed 的处理方式(PingPong 模式下)就容易理解了,有兴趣的朋友可以细看一下.