どうもこんにちはカレンダー主催者です。4日の遅刻ですさーせんm(_ _)m
最近C#のほうでやってるもんで、C++の書き方といい、エラーといいで悩まされてました。
本題はいります
基本設計
DXライブラリとともによく出てくるライブラリ「Siv3D」(C++/DirectX 12)には、Effect
クラスが存在します。そちらは、こちらを参照してください。
今回は、これぐらい気軽にエフェクトを扱えるようにしたいと思います。言語はC++です。Cでやる場合は頑張れ。
設計としては、
エフェクト管理にエフェクトを追加
↓
メインループ内でエフェクトが有効な間、実行する
こんな感じです。
ちなみにDxLibじゃなくても動きます。
実装
突貫でやったんでヘッダーにコード直書きです。
管理クラス
#include <vector>
#include <functional>
class Effect {
private:
std::vector<std::function<bool(int)>> EffectList;
std::vector<bool> Valid, Finished;
std::vector<int> Counter;
public:
/// <summary>
/// エフェクトを有効にする
/// </summary>
/// <param name="UpdateFunction">エフェクト実体</param>
/// <returns>エフェクトのインデックス</returns>
int Add(std::function<bool(int)> UpdateFunction) {
EffectList.push_back(UpdateFunction);
Counter.push_back(0);
Valid.push_back(true);
Finished.push_back(false);
return EffectList.size() - 1;
}
/// <summary>
/// エフェクトの一時停止
/// </summary>
/// <param name="index">一時停止するエフェクトのインデックス</param>
void Pause(int index) {
if (index >= EffectList.size() || Finished[(int)index]) return;
Valid[(int)index] = false;
}
/// <summary>
/// 一時停止したエフェクトの再開
/// </summary>
/// <param name="index">再開するエフェクトのインデックス</param>
void Resume(int index) {
if (index >= EffectList.size() || Finished[(int)index]) return;
Valid[(int)index] = true;
}
/// <summary>
/// エフェクトの停止
/// </summary>
/// <param name="index">停止するエフェクトのインデックス</param>
void Stop(int index) {
if (index >= EffectList.size() || Finished[(int)index]) return;
Valid[(int)index] = false;
Finished[(int)index] = true;
}
/// <summary>
/// 停止されているエフェクトを一番最初から再開する
/// </summary>
/// <param name="index">再開するエフェクトのインデックス</param>
void Restart(int index) {
Counter[(int)index] = 0;
Valid[(int)index] = true;
Finished[(int)index] = false;
}
/// <summary>
/// <para>エフェクトを処理する</para>
/// <para>メインループ内で呼ぶ</para>
/// </summary>
void Update() {
if (EffectList.size() == 0) {
return;
}
for (int i = 0; EffectList.size() > i; i++) {
if (!Valid[i]) {
continue;
}
bool r = EffectList[i](Counter[i]); //trueのときに終了する
Counter[i]++;
if (r) {
Finished[i] = true;
Valid[i] = false;
}
}
}
};
#include "DxLib.h"
#include "Effect.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
if (DxLib_Init() == -1){
return -1;
}
ChangeWindowMode(TRUE);
SetDrawScreen(DX_SCREEN_BACK);
Effect effect;
while (ScreenFlip() == 0 && ClearDrawScreen() == 0 && ProcessMessage() == 0) {
effect.Add([](int t) { //エフェクトの実体が定義された関数を渡す ラムダ式で直接実装することも可能
DrawPixel(t, t, 0xffffff);
return false;//終了条件
});
effect.Update();
}
DxLib_End();
return 0;
}
画面に(0, 0)から斜め45度に線が入っていくと思います。
応用
画像を一定時間ごとに点滅させてみましょう。
実装
class TextureFlash {
int Time, Interval;
int CenterPosX, CenterPosY;
std::vector<byte> FlashPattern;
double Rate, Angle;
int texHandle;
public:
TextureFlash(int CenterPosX, int CenterPosY, std::string texture, int Count, int Interval, std::vector<byte> FlashAlphaPattern, double Rate = 1.0, double Angle = 0) {
Time = Count; //実行時間
this->Interval = Interval; //点滅の間隔
this->CenterPosX = CenterPosX; // 画像の中心座標
this->CenterPosY = CenterPosY; // 画像の中心座標
this->Rate = Rate; // 画像の拡大比率
this->Angle = Angle; // 画像の回転角度
texHandle = LoadGraph(texture.c_str());
FlashPattern = FlashAlphaPattern;
}
bool Update(int Counter) {
SetDrawBlendMode(DX_BLENDMODE_ALPHA, FlashPattern[(Counter / Interval) % FlashPattern.size()]);
DrawRotaGraph(CenterPosX, CenterPosY, Rate, Angle, texHandle, true);
if (Time == 0) return false; // Time == 0のとき、常に実行
return Counter >= Time;
}
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
if (DxLib_Init() == -1)
return -1;
ChangeWindowMode(TRUE);
SetDrawScreen(DX_SCREEN_BACK);
Effect effect;
TextureFlash flash(320, 240, "Grapic.png", 0, 60, { 255, 0 }); // 透明度を0%と100%で切り替える
effect.Add([&](int t) { return flash.Update(t); });
while (ScreenFlip() == 0 && ClearDrawScreen() == 0 && ProcessMessage() == 0) {
effect.Update();
}
DxLib_End();
return 0;
}
これで画像が、画面の中央で一定間隔で無限に点滅してるかと思います。
実行結果
GIFのいい感じの取り方わからなかったんでGyazoに上げてます。
サンプル映像
最後に
今回は画像の点滅を実装しましたが、フェードイン/アウトや、一定時間ごとに画像の切り替えでアニメーションを実装できたりと、できることの幅は広いです。様々なエフェクトやアニメーションを実装してみてください。
最後にC#版も載せておきます
using System.Collections.Generic;
using System;
namespace DxLibEffect
public class EffectAdmin
{
private List<Func<int, bool>> EffectList = new List<Func<int, bool>>();
private List<bool> Valid = new List<bool>();
private List<bool> Finished = new List<bool>();
public uint EffectCount { get { return (uint)EffectList.Count; } }
private List<int> Counter = new List<int>();
public uint Fire(Func<int, bool> UpdateFunc)
{
EffectList.Add(UpdateFunc);
Counter.Add(0);
Valid.Add(true);
Finished.Add(false);
return (uint)EffectList.Count - 1;
}
public void Pause(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = false;
}
public void Resume(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = true;
}
public void Stop(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = false;
Finished[(int)index] = true;
}
public void Restart(uint index)
{
Counter[(int)index]= 0;
Valid[(int)index] = true;
Finished[(int)index] = false;
}
public void Update()
{
if(EffectList == null)
{
return;
}
for(var i = 0; EffectList.Count > i; i++)
{
if (!Valid[i])
{
continue;
}
bool r = EffectList[i](Counter[i]);
Counter[i]++;
if (r)
{
Finished[i] = true;
Valid[i] = false;
}
}
}
}
}
正直C#が使いやすくてですね、それでしばらくC++を触ってなかったわけです。
using System.Collections.Generic;
using System;
namespace DxLibEffect
public class EffectAdmin
{
private List<Func<int, bool>> EffectList = new List<Func<int, bool>>();
private List<bool> Valid = new List<bool>();
private List<bool> Finished = new List<bool>();
public uint EffectCount { get { return (uint)EffectList.Count; } }
private List<int> Counter = new List<int>();
public uint Fire(Func<int, bool> UpdateFunc)
{
EffectList.Add(UpdateFunc);
Counter.Add(0);
Valid.Add(true);
Finished.Add(false);
return (uint)EffectList.Count - 1;
}
public void Pause(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = false;
}
public void Resume(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = true;
}
public void Stop(uint index)
{
if (index >= EffectCount || Finished[(int)index]) return;
Valid[(int)index] = false;
Finished[(int)index] = true;
}
public void Restart(uint index)
{
Counter[(int)index]= 0;
Valid[(int)index] = true;
Finished[(int)index] = false;
}
public void Update()
{
if(EffectList == null)
{
return;
}
for(var i = 0; EffectList.Count > i; i++)
{
if (!Valid[i])
{
continue;
}
bool r = EffectList[i](Counter[i]);
Counter[i]++;
if (r)
{
Finished[i] = true;
Valid[i] = false;
}
}
}
}
}