日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SDL游戏开发之七-虚拟摇杆

發布時間:2023/12/14 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SDL游戏开发之七-虚拟摇杆 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

當下的智能機除了音量鍵、Home鍵外,幾乎沒有多余的按鍵,因而有一部分游戲提供了虛擬搖桿。

SDL2.x并沒有提供虛擬搖桿相關的代碼,不過實現起來并不算困難;虛擬搖桿包括繪圖和事件處理:前者提供視覺效果,后者則捕獲事件并作出響應。

示例結果如下:

圖1-虛擬搖桿演示程序

?本示例中大約有四個類:

  • Game類 主要包含了初始化、事件處理、紋理繪制等功能,是本示例的核心類;
  • Player類 玩家類,搖桿控制玩家進行移動,主要負責顯示;
  • ShotStick類 虛擬搖桿類,接收事件并處理事件;
  • TextureManager類 紋理管理單例類,負責加載紋理,并把圖片按照鍵值對保存。
  • ?在SDL游戲開發之六-簡單的SDL程序一節中,有對Game類以及SDL的游戲流程做了一個簡單的介紹,對于Game類的結構在這里不再贅述。

    1. Game類

    首先簡單地說明一下Game的函數。

    1.1 Game::init

    bool Game::init(const char *title, int xpos, int ypos, int width, int height, int flags) {m_bRunning = false;if (SDL_Init(SDL_INIT_EVERYTHING) == 0){/// if succeeded create our windowm_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags);if (m_pWindow != NULL)m_pRenderer = SDL_CreateRenderer(m_pWindow, -1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);if (m_pRenderer != NULL)SDL_SetRenderDrawColor(m_pRenderer,210,250,255,255);elsereturn false;}elsereturn false;

    先對SDL庫進行初始化,然后創建了窗口和渲染器。

    m_bRunning = true;std::string platform = SDL_GetPlatform();// initif (platform == "Android") {SDL_GetWindowSize(m_pWindow,&m_gameWidth,&m_gameHeight);}else {m_gameWidth = width;m_gameHeight = height;}SDL_Log("width=%d, height=%d\n", m_gameWidth, m_gameHeight);

    在桌面操作系統下如windows,SDL會根據傳遞的窗口大小進行創建窗口;而對于搭載了android系統等的手機來說,其窗口大小就是手機的分辨率。(當然也是可以通過SDL_SetScale設置縮放比,不過會造成圖片不同程度的拉伸)

    //結合SDL_RendererTheTextureManager::Instance()->bind(m_pRenderer);/*加載圖片資源*/try{TheTextureManager::Instance()->load("Resources/icon.png","player");TheTextureManager::Instance()->load("Resources/shotStick1.png","shotStick1");TheTextureManager::Instance()->load("Resources/shotStick.png","shotStick2");}catch (std::runtime_error& e){std::cout << e.what() << std::endl;return false;}m_pPlayer = new Player();m_pPlayer->init("player");m_pShotStick = new ShotStick();m_pShotStick->init("shotStick1", "shotStick2");return true;

    在Game::init函數的后半段,先是加載了所需要的圖片資源,然后創建了一個玩家對象和虛擬搖桿對象。

    1.2 Game::render

    void Game::render() {SDL_SetRenderDrawColor(m_pRenderer,210,250,255,255);///clear the renderer to the draw colorSDL_RenderClear(m_pRenderer);///drawSDL_SetRenderDrawColor(m_pRenderer, 0, 0, 0, 255);SDL_Rect rect = { 0, 0, 200, 200 };SDL_RenderDrawRect(m_pRenderer, &rect);m_pPlayer->draw(m_pRenderer);m_pShotStick->draw(m_pRenderer);///draw to the screenSDL_RenderPresent(m_pRenderer); }

    Game::render()負責渲染。后繪制的圖片可能會遮擋之前繪制的圖片,所以需要確認好繪制次序(其中的幾句代碼還繪制了一個黑色矩形,與本例無關,只是提醒在SDL中繪制圖形,需要先把渲染器的繪制顏色和清屏顏色不同才行)

    1.3 Game::handleEvents

    void Game::handleEvents() {SDL_Event event;while (SDL_PollEvent(&event)){switch (event.type){case SDL_QUIT:m_bRunning = false;break;}m_pShotStick->handleEvents(&event);}auto velocity = m_pShotStick->getVelocity();m_pPlayer->setVelocity(velocity); }

    Game::handlerEvents()負責處理。有事件發生時則會交給搖桿對象進行處理,之后再根據搖桿對象的偏移程度來更改操作玩家對象。

    1.4 Game::update

    void Game::update() {m_pPlayer->update(); }

    Game::handleEvents()只是負責設置玩家的速度,在update函數中還需要把速度乘以時間轉為距離。

    2. ShotStick 搖桿類

    ?

    2.1 初始化函數

    bool ShotStick::init(const std::string& rockerID, const std::string& backgroundID) {m_rockerID = rockerID;m_backgroundID = backgroundID;//獲取搖桿rectSDL_Rect rect1 = TheTextureManager::Instance()->getTextureRectFromId(rockerID);SDL_Rect rect2 = TheTextureManager::Instance()->getTextureRectFromId(backgroundID);//圓半徑m_outCircle.radius = rect1.w/2;m_inCircle.radius = rect2.w/2;return true; }

    init函數會保存搖桿和搖桿背景圖片的鍵名,以便于確定位置和進行繪制。

    2.2 繪制函數

    void ShotStick::draw(SDL_Renderer * ren) {// 畫出虛擬搖桿畫出外圓int screenW = TheGame::getInstance()->getGameWidth();int screenH = TheGame::getInstance()->getGameHeight();// 圓心m_inCircle.x = m_outCircle.x = m_outCircle.radius;m_inCircle.y = m_outCircle.y = screenH - m_outCircle.radius;TheTextureManager::Instance()->draw(m_rockerID,m_outCircle.x - m_outCircle.radius,m_outCircle.y - m_outCircle.radius);// 畫出內圓TheTextureManager::Instance()->draw(m_backgroundID,m_inCircle.x + m_relativePoint.x - m_inCircle.radius,m_inCircle.y + m_relativePoint.y - m_inCircle.radius); }

    虛擬搖桿默認顯示在左下角。

    2.3 ShotStick::handleEvents

    void ShotStick::handleEvents(SDL_Event* event) {switch (event->type){case SDL_MOUSEBUTTONDOWN:{if (event->button.button != SDL_BUTTON_LEFT)break;Sint32 x = event->motion.x, y = event->motion.y;auto distance = std::sqrt(std::pow(x - m_outCircle.x, 2) + std::pow(y - m_outCircle.y, 2));//超出距離if (distance > m_outCircle.radius) {m_fingerId = -1;break;}elsem_fingerId = 0;}

    鼠標左鍵按下時會觸發SDL_MOUSEBUTTONDOWN并且event->button.button的值是SDL_BUTTON_LEFT。

    ?為便于在電腦上調試,虛擬搖桿會接收鼠標左鍵事件,判斷按下的點是否在圖2所示的半透明背景內。只有在背景圓內才表示一次有效的按鍵。

    圖2 虛擬搖桿

    ?

    case SDL_MOUSEMOTION:{if (m_fingerId == -1)break;Sint32 x = event->motion.x, y = event->motion.y;//保證不會超過搖桿背景圓double x_average = x - m_outCircle.x;double y_average = y - m_outCircle.y;double d = std::sqrt(std::pow(x_average, 2) + std::pow(y_average, 2));double m = d > m_outCircle.radius ? m_outCircle.radius : d;m_relativePoint.x = m * (x_average / d);m_relativePoint.y = m * (y_average / d);}break;

    ?當處理SDL_MOUSEBUTTONDOWN事件中產生了一個有效的點擊后,再發生移動則會計算搖桿的圓心到搖桿背景圓心的距離,得到的值保存到m_relativePoint中。

    case SDL_MOUSEBUTTONUP:{if (event->button.button != SDL_BUTTON_LEFT)break;m_fingerId = -1;m_relativePoint.x = 0.0;m_relativePoint.y = 0.0;}break;

    ?當產生鼠標左按鍵松開后,搖桿歸零。

    2.4 ShotStick::getVelocity

    Vector2D ShotStick::getVelocity() {double x = m_relativePoint.x;double y = m_relativePoint.y;double r = std::sqrt(x * x + y * y);if (r == 0)return Vector2D(0, 0);/* sin = y/r; cos = x/r; */r = m_outCircle.radius;return Vector2D(x / r, y / r); }

    ?

    圖3 偏移的計算

    如圖3所示,O2減去O1則是m_relativePoint的值,再根據勾股定理則可以知道r的值。當r==0時,m_relativePoint=(0, 0);在r != 0后,又把r賦值為搖桿背景圓的半徑大小,這樣能保證其值域在[0, 1]之間。這樣做的好處就是返回的速度不僅和方向有關,還和速度有關。當不為0時,則返回歸一化后的值(值域[0, 1])。

    后續還有則是在移動平臺下的處理,效果同上類似,應該沒什么問題(很久之前測試過)

    case SDL_FINGERDOWN:{SDL_Finger finger;finger.x = event->tfinger.x * TheGame::getInstance()->getGameWidth();finger.y = event->tfinger.y * TheGame::getInstance()->getGameHeight();// 綁定有效idif (m_fingerId == -1&& std::sqrt(std::pow(finger.x - m_outCircle.x, 2) +std::pow(finger.y - m_outCircle.y,2)) <= m_outCircle.radius)m_fingerId = event->tfinger.fingerId;}case SDL_FINGERMOTION:{SDL_FingerID id = event->tfinger.fingerId;// 如果不相等,退出if (m_fingerId != id)break;SDL_Finger finger;finger.id = id;finger.x = event->tfinger.x * TheGame::getInstance()->getGameWidth();finger.y = event->tfinger.y * TheGame::getInstance()->getGameHeight();double x_average = finger.x - m_outCircle.x;double y_average = finger.y - m_outCircle.y;double d = std::sqrt(std::pow(x_average, 2) + std::pow(y_average, 2));double m = d > m_outCircle.radius ? m_outCircle.radius : d;m_relativePoint.x = m * (x_average / d);m_relativePoint.y = m * (y_average / d);}break;case SDL_FINGERUP:{SDL_FingerID id = event->tfinger.fingerId;// 如果為有效idif (id == m_fingerId){m_relativePoint.x = 0;m_relativePoint.y = 0;m_fingerId = -1;}}break;

    移動平臺下和桌面操作系統類似,只不過移動平臺下一般為多點觸碰,所以也是需要綁定SDL_Finger的id的(類似于只有鼠標左鍵才能操作)。

    3. Player類

    玩家類則相對比較簡單,只是負責顯示精靈和確認位置而已。

    bool Player::init(const string& spriteID) {m_spriteID = spriteID;return true; }void Player::setVelocity(const Vector2D& velocity) {m_velocity = velocity; }void Player::draw(SDL_Renderer*ren) {TheTextureManager::Instance()->draw(m_spriteID,(int)m_position.getX(),(int)m_position.getY()); }void Player::update() {m_velocity *= 2;m_position += m_velocity;//m_velocity += m_acceleration; }void Player::clean() { }

    代碼:https://github.com/sky94520/ShotStick

    總結

    以上是生活随笔為你收集整理的SDL游戏开发之七-虚拟摇杆的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。