🔊

DXライブラリでADX2LEを使って音量調整オプションを作るまで[3.音量調整実装編]

2024/11/20に公開

前書き

前回まで「ツールを使ったサウンドデータ作成」「ADXでビルドしたデータをDXライブラリ上で再生」まで行いました。
https://zenn.dev/takodevlog/articles/6b4a11d4991991
https://zenn.dev/takodevlog/articles/8c80bdecd88730

今回は締めとして、スライダーを使った音量調整を実装していきます。
ほぼコードです。

マウス処理の実装

スライダーをマウスでつまんで音量調整をしたいので、キーボードに続いてマウスのクラスも実装します。

Mouse.h
#pragma once
class Mouse
{
public:
    Mouse();
    virtual ~Mouse() = default;

    /// <summary>更新処理</summary>
    void Update();

    /// <summary>押された瞬間か</summary>
    /// <param name="button">DxLibキー指定マクロ</param>
    bool IsButtonDown(int button) const;

    /// <summary>押している状態か</summary>
    /// <param name="button">DxLibキー指定マクロ</param>
    bool IsButtonHold(int button) const;

    /// <summary>離された瞬間か</summary>
    /// <param name="button">DxLibキー指定マクロ</param>
    bool IsButtonUp(int button) const;

    /// <summary>マウスX座標取得</summary>
    int GetMouseX() const { return m_pos_x_; }

    /// <summary>マウスY座標取得</summary>
    int GetMouseY() const { return m_pos_y_; }

private:
    int m_input_;       // 現在の入力状態
    int m_prev_input_;  // 1フレーム前の入力状態
    int m_pos_x_;       // X座標
    int m_pos_y_;       // Y座標
};
Mouse.cpp
#include "DxLib.h"
#include "Mouse.h"

Mouse::Mouse()
    : m_input_(0)
    , m_prev_input_(0)
    , m_pos_x_(0)
    , m_pos_y_(0)
{}

void Mouse::Update()
{
    m_prev_input_ = m_input_;
    GetMousePoint(&m_pos_x_, &m_pos_y_);
    m_input_ = GetMouseInput();
}

// ボタンを押した瞬間
bool Mouse::IsButtonDown(int button) const
{
    if ((m_input_ & button) && !(m_prev_input_ & button)) { return true; }
    return false;
}

// ボタンを押し続けているか
bool Mouse::IsButtonHold(int button) const
{
    if ((m_input_ & button) && (m_prev_input_ & button)) { return true; }
    return false;
}

// ボタンを離した瞬間
bool Mouse::IsButtonUp(int button) const
{
    if (!(m_input_ & button) && (m_prev_input_ & button)) { return true; }
    return false;
}

マウスのX座標・Y座標を取得する処理と、クリック判定系のみです。

スライダーの実装

今回の画面的な主役であるスライダーのクラスを実装します。

Slider.h
#pragma once
#include <string>
#include "DxLib.h"

class Mouse;

class Slider
{
public:
	/// <summary>コンストラクタ</summary>
	/// <param name="value">初期値</param>
	/// <param name="min">最小値</param>
	/// <param name="max">最大値</param>
	/// <param name="pos_x">X座標</param>
	/// <param name="pos_y">Y座標</param>
	/// <param name="name">スライダー識別名</param>
	Slider(float value, float min, float max, float pos_x, float pos_y, std::string name);

	virtual ~Slider() = default;

	/// <summary>値のセットと是正(clamp)</summary>
	void SetValue(float set_value);

	/// <summary>ハンドルのサイズ設定</summary>
	void SetHandleSize(float set_handle_size);

	/// <summary>ハンドルの色設定</summary>
	void SetHandleColor(int set_handle_color);

	/// <summary>ハンドルの有効無効切り替え</summary>
	void SetHandleEnabled(bool set_handle_enabled);

	/// <summary>スライダーのボックスサイズ(半分)</summary>
	/// <param name="set_back_width">幅</param>
	/// <param name="set_back_height">高さ</param>
	void SetBackSize(float set_back_width, float set_back_height);

	/// <summary>スライダー背景色設定</summary>
	/// <param name="set_back_color">背景色</param>
	void SetBackColor(int set_back_color);

	/// <summary>スライダー塗りつぶし色設定</summary>
	/// <param name="set_back_color">塗りつぶし色</param>
	void SetFillColor(int set_fill_color);

	/// <summary>スライダーの値取得</summary>
	float GetValue() const { return m_value_; }

	/// <summary>スライダーのハンドル有効無効取得</summary>
	bool GetHandleEnabled() const { return m_handle_enabled_; }

	/// <summary>更新処理</summary>
	/// <param name="mouse">マウスインスタンスポインター</param>
	void Update(const Mouse* mouse);

	/// <summary>描画処理</summary>
	void Render();

	// コピー系削除
	Slider(const Slider&) = delete;
	Slider& operator = (const Slider&) = delete;

private:
	/// <summary>スライダーの最小値・最大値セット</summary>
	/// <param name="set_min_value">最小値</param>
	/// <param name="set_max_value">最大値</param>
	void SetMinMaxValue(float set_min_value, float set_max_value);

	/// <summary>ハンドルを掴んだか</summary>
	/// <param name="mouse_x">マウスX座標</param>
	/// <param name="mouse_y">マウスY座標</param>
	bool IsHandleHold(float mouse_x, float mouse_y) const;

	const int kDefaultHandleRadius = 12;
	const int kDefaultHandleColor = GetColor(255, 255, 255);
	const int kDefaultBarWidth = 100;
	const int kDefaultBarHeight = 5;
	const int kDefaultBackColor = GetColor(200, 200, 200);
	const int kDefaultFillColor = GetColor(0, 225, 225);

	VECTOR m_position_;
	VECTOR m_back_size_;
	VECTOR m_handle_position_ = {};
	int m_handle_color_;
	int m_back_color_;
	int m_fill_color_;
	bool m_handle_enabled_;
	bool m_is_handle_move_ = false;
	float m_handle_size_;
	float m_value_;
	float m_max_value_ = 1;
	float m_min_value_ = 0;
	std::string m_name_;

	// 現在掴まれているスライダー名の識別用
	static std::string m_current_name_;
};
Slider.cpp
#include <algorithm>
#include "Slider.h"
#include "Mouse.h"

std::string Slider::m_current_name_ = "";

Slider::Slider(float value, float min, float max, float pos_x, float pos_y, std::string name)
    : m_is_handle_move_(false)
    , m_position_{ pos_x, pos_y }
    , m_name_(name)
{
    SetHandleColor(kDefaultHandleColor);
    SetBackColor(kDefaultBackColor);
    SetFillColor(kDefaultFillColor);
    SetHandleSize(kDefaultHandleRadius);
    SetBackSize(kDefaultBarWidth, kDefaultBarHeight);
    SetMinMaxValue(min, max);
    SetValue(value);
}

// 値の設定(clamp)
void Slider::SetValue(float set_value)
{
    m_value_ = std::clamp(set_value, m_min_value_, m_max_value_);
}

// ハンドルのサイズ設定
void Slider::SetHandleSize(float set_handle_size)
{
    m_handle_size_ = set_handle_size;
}

// ハンドル色設定
void Slider::SetHandleColor(int set_handle_color) {
    m_handle_color_ = set_handle_color;
}

// ハンドル有効無効設定
void Slider::SetHandleEnabled(bool set_handle_enabled)
{
    m_handle_enabled_ = set_handle_enabled;
}

// スライダー背景サイズ設定
void Slider::SetBackSize(float set_back_width, float set_back_height)
{
    m_back_size_ = { set_back_width, set_back_height };
}

// スライダー背景色設定
void Slider::SetBackColor(int set_back_color)
{
    m_back_color_ = set_back_color;
}

// スライダー塗りつぶし色設定
void Slider::SetFillColor(int set_fill_color)
{
    m_fill_color_ = set_fill_color;
}

void Slider::Update(const Mouse* mouse)
{
    // ハンドルがクリックされている状態チェック
    if (IsHandleHold(mouse->GetMouseX(), mouse->GetMouseY()) && mouse->IsButtonDown(MOUSE_INPUT_LEFT))
    {
        m_is_handle_move_ = true;
        m_current_name_ = m_name_;
    }
    else if (m_is_handle_move_ && mouse->IsButtonHold(MOUSE_INPUT_LEFT) && m_current_name_ == m_name_)
    {
        m_is_handle_move_ = true;
    }
    else
    {
        m_is_handle_move_ = false;
    }

    if (m_is_handle_move_)
    {
        m_handle_position_.x = mouse->GetMouseX();

        if (m_handle_position_.x > m_position_.x + m_back_size_.x) { m_handle_position_.x = m_position_.x + m_back_size_.x; }
        else if (m_handle_position_.x < m_position_.x - m_back_size_.x) { m_handle_position_.x = m_position_.x - m_back_size_.x; }

        SetValue((m_handle_position_.x - (m_position_.x - m_back_size_.x)) / (m_back_size_.x * 2.0f) * (m_max_value_ - m_min_value_) + m_min_value_);
    }
    else
    {
        m_handle_position_ =
        {
            (m_position_.x - m_back_size_.x) + (m_value_ - m_min_value_) / (m_max_value_ - m_min_value_) * (m_back_size_.x * 2),
            m_position_.y,
        };
    }
}

void Slider::Render()
{
    // 背景描画
    DrawBox(
        m_position_.x - m_back_size_.x, m_position_.y - m_back_size_.y,
        m_position_.x + m_back_size_.x, m_position_.y + m_back_size_.y,
        m_back_color_, true);

    // スライダー(塗りつぶし)描画
    DrawBox(
        m_position_.x - m_back_size_.x + 1, m_position_.y - m_back_size_.y + 1,
        m_handle_position_.x - 1, m_position_.y + m_back_size_.y - 1,
        m_fill_color_, true);

    // ハンドル描画
    if (GetHandleEnabled())
    {
        DrawCircle(m_handle_position_.x, m_handle_position_.y,
            m_handle_size_, m_handle_color_, true);
    }
}

// 最小値最大値設定
void Slider::SetMinMaxValue(float set_min_value, float set_max_value)
{
    if (set_min_value >= m_max_value_) { set_min_value = m_max_value_ - 1; }
    m_min_value_ = set_min_value;

    if (set_max_value <= m_min_value_) { set_max_value = m_min_value_ + 1; }
    m_max_value_ = set_max_value;
}

// カーソルがスライダーに乗っているか
bool Slider::IsHandleHold(float mouse_x, float mouse_y) const
{
    float vx = m_handle_position_.x - mouse_x;
    float vy = m_handle_position_.y - mouse_y;

    if (vx < 0) { vx *= -1; }
    if (vy < 0) { vy *= -1; }

    if (vx < m_handle_size_ && vy < m_handle_size_) { return true; }

    return false;
}

カテゴリごとに音量を変える方法

指定カテゴリの音量を設定するには、
criAtomExCategory_SetVolumeByName という関数を使用します。
引数は2つで、1つ目がカテゴリ名、2つ目がボリューム数値です。
ボリュームの数値は0.0~5.0まで設定が可能ですが、1.0以降は0dBを超えて音割れの可能性があるため、
今回は0.0~1.0の範囲で設定できるようにしてみます。

ではMain.cppにマウスとスライダーと共に設置していきます。
今回も長いので前後の行と合わせて差分で載せます。

Main.cpp
#include <memory>
#include "DxLib.h"
#include "Keyboard.h"
+ #include "Mouse.h"
+ #include "Slider.h"

︙

#define MAX_CRIFS_LOADER    (64)

+ // 音量の最大最小
+ #define VOLUME_MAX (1.0f)
+ #define VOLUME_MIN (0.0f)

// エラーコールバック関数

︙

    // 入力用
    std::unique_ptr<Keyboard> p_keyboard = std::make_unique<Keyboard>();
+   std::unique_ptr<Mouse> p_mouse = std::make_unique<Mouse>();

+   // スライダー作成
+   Slider master_slider{ 1.0f, VOLUME_MIN, VOLUME_MAX, 400.0f, 250.0f, "Master" };
+   Slider bgm_slider{ 1.0f, VOLUME_MIN, VOLUME_MAX, 400.0f, 350.0f, "BGM" };
+   Slider se_slider{ 1.0f, VOLUME_MIN, VOLUME_MAX, 400.0f, 450.0f, "SE" };

    // DxLib初期設定
        // 入力の更新
        p_keyboard->Update();
+       p_mouse->Update();

+       // スライダーの更新
+       master_slider.Update(p_mouse.get());
+       bgm_slider.Update(p_mouse.get());
+       se_slider.Update(p_mouse.get());

        // サーバ処理の実行
        // 停止
        if (p_keyboard->IsKeyDown(KEY_INPUT_SPACE))
        {
            criAtomExPlayer_Stop(player);
        }

+       // 音量の設定(動作検証のため毎フレーム更新)
+       criAtomExCategory_SetVolumeByName("Master", master_slider.GetValue());
+       criAtomExCategory_SetVolumeByName("BGM", bgm_slider.GetValue());
+       criAtomExCategory_SetVolumeByName("SE", se_slider.GetValue());
        DrawString(20, 560, "Esc:Exit", GetColor(255, 255, 255));

+       DrawFormatString(300, 270, GetColor(255, 255, 255), "マスター音量:%f", master_slider.GetValue());
+       master_slider.Render();
+       DrawFormatString(300, 370, GetColor(255, 255, 255), "BGM音量:%f", bgm_slider.GetValue());
+       bgm_slider.Render();
+       DrawFormatString(300, 470, GetColor(255, 255, 255), "SE音量:%f", se_slider.GetValue());
+       se_slider.Render();
        ScreenFlip();

実行すると以下のような画面になるはずです。

スライダーを実際につまんで左右することでそれぞれ音量が変わると思います。

おわり

これでUnityのAudioMixerを用いて作っていた音量調節と同じようなものをDXライブラリ上で再現することができました。
DXライブラリのみでSoundManagerのようなクラスを作ってゴリゴリ実装するのも良いと思いますが、
ADX2LEを使用することでカテゴリごとの音量調整ができるだけでなく、ABループやキューを使った再生音のランダム化など幅広い表現が簡単に実装できるようになります。
ツールを覚える手間はありますが、サウンド関連のクオリティを一段上げるのにはかなり役立つと思うので、ミドルウェアも上手く使っていけると良いですね。

参考記事

DXライブラリ上でUnity風のスライダーを作る際に大いに参考にさせてもらいました。
https://piyosaburo0129.blog.jp/archives/28041309.html

ありがとうございました🙇

Discussion