開發環境 : 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(¤tMousePoint, &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;
// window
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("Can not init video, %s", SDL_GetError());
return 1;
}
window = SDL_CreateWindow(
"鼠標事件",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WIDTH,HEIGHT,
SDL_WINDOW_SHOWN
);
if (window==NULL) {
SDL_Log("Can not create window, %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("Can not create renderer, %s", SDL_GetError());
}
// 創建
rect = RectShape_Create(0, 0, 80, 60, 0xffff0000);
event_loop();
// 銷毀
RectShape_Destroy(rect);
/* 釋放渲染器 */
SDL_DestroyRenderer(renderer);
/* 釋放窗口 */
SDL_DestroyWindow(window);
return 0;
}