邱 璇洛 (ゝ∀・)

邱 璇洛 (ゝ∀・)

你好哇(*゚∀゚*)~这里是邱璇洛的博客,常常用来记录一些技术文章和小日常~(σ゚∀゚)σ
twitter
tg_channel

SDL2学習ノート - マウスイベントとドラッグ

開発環境 : MacOS
参考資料 : 陳雲先生の SDL2 チュートリアル

SDL2 でマウスイベントを実装するのは比較的簡単で、event_loop(イベントループ)switchまたはifを追加するだけで実現できます:

/* イベントループ */
void event_loop() {
    while (1) {

        ...

        // マウスイベント
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) {
                return;
            } 
        }

        ...
    }
}

しかし、ドラッグイベントを実装するには、大幅な改造が必要です。
まず、オブジェクト指向の考え方を使ってRectShapeを抽象化し、ヘッダーファイルに構造体といくつかの必須関数を作成します。

// 抽象矩形
#ifndef MOUSEEVENT_RECTSHAPE_H
#define MOUSEEVENT_RECTSHAPE_H

#include <SDL.h>

typedef struct {
    SDL_FRect dest;
    uint32_t color;
    // _は内部使用を示す
    // 開始点を記録
    SDL_FPoint _dragStartPoint;
    // ドラッグ開始時のマウスの位置を記録(マウスの移動距離を理解するため)
    SDL_FPoint _dragStartMousePoint;
    // クリックされたかどうか(有効かどうか)
    int _dragEnabled;
} RectShape;

// 矩形を作成
RectShape *RectShape_Create(float x, float y, float w, float h, uint32_t color);

// 描画
void RectShape_Draw(RectShape *self, SDL_Renderer *renderer);

// マウスイベント .. SDL_Eventはイベントタイプ
void RectShape_OnMouseEvent(RectShape *self, SDL_Event *event);

// 破棄
void RectShape_Destroy(RectShape *self);

#endif

次に、これを一つずつ実装します:

最初は矩形を作成することです。これは比較的簡単です:

...

// 矩形を作成
RectShape *RectShape_Create(float x, float y, float w, float h, uint32_t color) {
    // メモリを割り当て
    RectShape *self = malloc(sizeof(RectShape));
    self->color = color;
    self->dest.x = x;
    self->dest.y = y;
    self->dest.w = w;
    self->dest.h = h;
    // デフォルトではドラッグ属性を無効にする
    self->_dragEnabled = 0;
    return self;
}

...

_dragEnabledはドラッグ属性が有効かどうかを決定し、マウスが押されていないときに属性を起動しないようにします(直接有効にすると自動的にルートを探し始めます)

次に、基本的な破棄と描画を行います。

...

// 描画
void RectShape_Draw(RectShape *self, SDL_Renderer *renderer) {
    // カラーチャンネルを抽出
    // 0xffff0000 ARGB
    SDL_SetRenderDrawColor(
        renderer,
        // RGBA
        // 赤チャンネルの値を抽出し、xxビット右にシフト
        (self->color & 0x00ff0000) >> 16, // 赤    r
        (self->color & 0x0000ff00) >> 8,  // 緑    g
        self->color & 0x000000ff,         // 青    b
        (self->color & 0xff000000) >> 24  // 透明  a
    );
    // &でアドレスを取得
    SDL_RenderFillRectF(renderer, &(self->dest));
}

...

// 破棄
void RectShape_Destroy(RectShape *self) {
    free(self);
}

dest->colorは直接使用できず、右シフト計算を使用して内部の要素(色 RGBA)を抽出する必要があります。

最も重要なことは、ドラッグイベントをどうやって実現するかです?

// マウスイベント(ドラッグ) .. SDL_Eventはイベントタイプ
void RectShape_OnMouseEvent(RectShape *self, SDL_Event *event) {
    switch (event->type) {
        // マウス移動イベント
        case SDL_MOUSEMOTION:
            // クリック時にマウスと一緒にドラッグを開始
            if (self->_dragEnabled == 1) {
                self->dest.x = self->_dragStartPoint.x + event->motion.x - self->_dragStartMousePoint.x;
                self->dest.y = self->_dragStartPoint.y + event->motion.y - self->_dragStartMousePoint.y;
            }
            break;
        // マウスボタン押下イベント
        case SDL_MOUSEBUTTONDOWN:
            // マウスが押されたとき、ドラッグを開始
            self->_dragStartPoint.x = self->dest.x;
            self->_dragStartPoint.y = self->dest.y;
            self->_dragStartMousePoint.x = event->button.x;
            self->_dragStartMousePoint.y = event->button.y;
            self->_dragEnabled = 1;
            break;
        // マウスボタン離上イベント
        case SDL_MOUSEBUTTONUP:
            self->_dragEnabled = 0;
            break;
    }
}

さらに、制限領域を設けて、正方形の領域だけをドラッグできるようにします。

// マウスイベント(ドラッグ) .. SDL_Eventはイベントタイプ
void RectShape_OnMouseEvent(RectShape *self, SDL_Event *event) {
    // 衝突を検出
    SDL_Point currentMousePoint;
    // 型変換
    SDL_Rect destRect;
    
    switch (event->type) {
        // マウス移動イベント
        case SDL_MOUSEMOTION:
            // クリック時にマウスと一緒にドラッグを開始
            if (self->_dragEnabled == 1) {
                self->dest.x = self->_dragStartPoint.x + event->motion.x - self->_dragStartMousePoint.x;
                self->dest.y = self->_dragStartPoint.y + event->motion.y - self->_dragStartMousePoint.y;
            }
            break;
        // マウスボタン押下イベント
        case SDL_MOUSEBUTTONDOWN:
            // 衝突を検出(マウスと赤いブロック)
            currentMousePoint.x = event->button.x;
            currentMousePoint.y = event->button.y;
            // 強制的に型変換
            destRect.x = (int) self->dest.x;
            destRect.y = (int) self->dest.y;
            destRect.w = (int) self->dest.w;
            destRect.h = (int) self->dest.h;
            // もし二つが交差するなら
            if (SDL_PointInRect(&currentMousePoint, &destRect)) {
                // マウスが押されたとき、ドラッグを開始
                self->_dragStartPoint.x = self->dest.x;
                self->_dragStartPoint.y = self->dest.y;
                self->_dragStartMousePoint.x = event->button.x;
                self->_dragStartMousePoint.y = event->button.y;
                self->_dragEnabled = 1;
            }
            break;
        // マウスボタン離上イベント
        case SDL_MOUSEBUTTONUP:
            self->_dragEnabled = 0;
            break;
    }
}

そして、main で小さな正方形を描画し、設定を行います。

#include <SDL.h>
#include "RectShape.h"

#define WIDTH 400
#define HEIGHT 300
/* フレームレートを設定 */
#define FRAMERATE 60

// レンダラー
SDL_Renderer *renderer; 
// ウィンドウ
SDL_Window *window;
// RectShape
RectShape *rect;


void draw() {
    // 背景を白色にレンダリングし、レンダラーを使用
    SDL_SetRenderDrawColor(renderer, 225, 225, 225, 225);
    SDL_RenderClear(renderer);

    RectShape_Draw(rect, renderer);

    // 画面に反映させる
    SDL_RenderPresent(renderer);
}

/* イベントループ */
void event_loop() {
    while (1) {
        /* フレームレート:ミリ秒値を取得 */
        long begin = SDL_GetTicks();
        /* アニメーション */
        draw();

        SDL_Event event;
        /* ループを使用してイベントを読み取る方がifより速い */
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                // マウス移動イベント
                case SDL_MOUSEMOTION:

                // マウスボタン押下イベント
                case SDL_MOUSEBUTTONDOWN:
                // マウスボタン離上イベント
                case SDL_MOUSEBUTTONUP:
                    // イベントを関数内部で処理する
                    RectShape_OnMouseEvent(rect, &event);
                    break;
                case SDL_QUIT: return;
            }
        }

        /* フレームレート:現在の値 */
        long current = SDL_GetTicks();
        /* 現在のフレームにかかる時間 */
        long cost = current - begin;
        /* 各フレームが必要な時間 */
        long frame = 1000/FRAMERATE;
        /* 休止する時間を計算してフレーム数を維持 */
        long delay = frame - cost;
        /* delayが負の場合は休止せずフレーム数を維持 */
        if (delay > 0) {
            SDL_Delay(delay);
        }
        /* システムリソースが十分な場合、十分なフレームレートを維持できるが、不足しても仕方がない */
    }
}

int main() {

    if (SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("ビデオの初期化に失敗しました, %s", SDL_GetError());
        return 1;
    }

    window = SDL_CreateWindow(
        "マウスイベント",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        WIDTH,HEIGHT,
        SDL_WINDOW_SHOWN
    );

    if (window==NULL) {
        SDL_Log("ウィンドウの作成に失敗しました, %s", SDL_GetError());
        return 1;
    }

    // レンダラーを作成
    // ウィンドウ レンダリングドライバ(インデックス) どのレンダリングを使用するか
    // SDL_RENDERER_ACCELERATED ハードウェアアクセラレーション GPU
    // SDL_RENDERER_SOFTWARE CPUレンダリング(何MC行動)
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer==NULL) {
        SDL_Log("レンダラーの作成に失敗しました, %s", SDL_GetError());
    }

    // 作成
    rect = RectShape_Create(0, 0, 80, 60, 0xffff0000);

    event_loop();
    // 破棄
    RectShape_Destroy(rect);
    /* レンダラーを解放 */
    SDL_DestroyRenderer(renderer);
    /* ウィンドウを解放 */
    SDL_DestroyWindow(window);
    return 0;
}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。