Sean Yih
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # SDL2: 控制 ###### tags: `SDL` ## 前言 隨者我們掌握圖形的顯示後,遊戲還需要讀取玩家的操作,做出相對應的回應,才能算是完整的遊戲。因此在這個章節,我將介紹以鍵盤和滑鼠控制遊戲的方式。 一個很重要的觀念是,我們並不是真正”控制”程式,而是”讀取”我們對鍵盤和滑鼠的動作,稱作**事件**,並指定若發生某事件,則呼叫某函數,藉此實現控制的效果。幸運的是,鍵盤和滑鼠動作的偵測之底層邏輯已經被建立好了!使用者只需要呼叫正確的函數讀取事件,並學習判斷事件的種類即可。 ## SDL_Event: 事件載體 SDL利用這個struct來儲存事件,這個struct可以儲存**所有你想的到的事件!** 所以在看documents的時候其實很恐怖,有一堆參數根本看不懂@@,在這裡我只會介紹鍵盤、滑鼠的使用。此外,它跟我們之前學的物件都不太一樣,**在於它只需要一個!** 不論你想偵測幾個事件,都只需要一個。 這特性跟它的底層邏輯有關,所有事件都被儲存在一個佇列(queue)中,可以想像事件們按照發生的先後排隊,然後我們每次只能從隊伍最前面抽取事件出來,再決定要如何處理。**SDL_Event並不是事件佇列本身,它擔任的角色只是那個抽取出來的物件的暫存區。** 所以一次只需要一個就夠了。 宣告的方式很簡單,如下: ```cpp SDL_Event event; ``` 簡單到我不會解釋。 ### SDL_FlushEvents: 清空佇列 在開始偵測之前,我們先學會清空佇列。這是因為各種事件會一直被加進佇列中,在遊戲開始前可能已經累積好幾百個你不希望偵測到的事件。例如: 很多人都會在等載入動畫的時候一直亂按鍵盤,沒清除掉事件的結果,就是你遊戲一進去角色就亂衝。因此在開始偵測想要的事件前,我們先呼叫它來清除之前的事件佇列。 語法如下: ```cpp void SDL_FlushEvents(Uint32 minType, Uint32 maxType); ``` 這裡的minType和maxType需要到documents去查表。SDL所有的**事件種類(EventType)** 都被按照某個順序排好,這個函數可以讓你清掉指定區間內的所有事件種類。聽起來很複雜,其實我只想全部清掉啊,所以我們用以下的程式去清掉**全部**。 ```cpp SDL_FlushEvents(SDL_FIRSTEVENT,SDL_LASTEVENT); ``` 這樣應該就很好理解first和last是什麼意思了吧! 對於有興趣深究清除事件種類的”順序”的人,[請點我](https://wiki.libsdl.org/SDL2/SDL_EventType)。 ### SDL_PollEvent: 取出事件 ```cpp int SDL_PollEvent(SDL_Event * event) ``` 這是這個函數的定義。它接收的參數只有一個SDL_Event的指標,這個函數會取出佇列中第一個元素,然後放進指標所指的物件,**並同步從佇列中移除**。 它的回傳值則是0或1,**1代表佇列中還有事件尚未被取出、處理。** 我們可以利用回傳值的特性,寫出一個常見的遊戲架構: ```cpp while (game_is_still_running) { SDL_Event event; while (SDL_PollEvent(&event)) { // poll 直到佇列清空! // 根據event種類決定response. } // 更新狀態、渲染畫面 } ``` 特別要注意在第二層while迴圈中,event 已經是有值的了,要使用時絕對不可以再呼叫一次SDL_PollEvent,否則會取到錯誤的事件! 如果需要同時接收多個event,則可以宣告更多SDL_Event物件去儲存,若一直使用同一個event做接收,則會有覆蓋的問題。**被覆蓋掉的event是無法回復的。** ### 事件種類 事件種類是SDL_Event裡面的一個元素(Uint32)。我們可以透過存取這個值來決定事件的種類。在SDL裡面有提供一個enum叫做SDL_EventType,常用的事件如下: | 名稱 | 事件內容 | | --- | --- | | SDL_QUIT | 退出。例如按下視窗上的”X”關閉按鈕,或是以指令結束程序。 | | SDL_KEYDOWN | 按下鍵盤。 | | SDL_KEYUP | 鬆開鍵盤。 | | SDL_MOUSEMOTION | 滑鼠移動。 | | SDL_MOUSEBUTTONDOWN | 按下滑鼠按鍵。 | | SDL_MOUSEBUTTONUP | 鬆開滑鼠按鍵。 | | SDL_MOUSEWHEEL | 滾動滑鼠滾輪。 | 實務上,我們常用一個switch的結構去處理事件種類。例如: ```cpp while (game_is_still_running) { SDL_Event event; while (SDL_PollEvent(&event)) { // poll 直到佇列清空! switch (event.type){ case SDL_QUIT: game_is_still_running = false; break; case SDL_KEYDOWN: //do something... default: continue; } } } ``` 事件種類必須在處理事件之前就決定好,否則會引發錯誤。例如: 程式要處裡Keyboard Event,卻被你餵一個Mouse Event給它,它就會出現取到NULL、甚至亂碼的情形。這種情況下很高機率會閃退。 --- 學會基礎的事件處理之後,我們接著深入探討兩種常用的控制機制: 鍵盤以及滑鼠。依照我個人的經驗,**鍵盤比較簡單。** 純鍵盤實踐的彈性也不會輸滑鼠太多,所以可以考慮用WASD或是上下左右鍵,去達成所有的使用者介面(UI)操作。 ## SDL_KeyboardEvent: 鍵盤事件 SDL_KeyboardEvent又是一個新的物件,它同時也是SDL_Event的一個member object。也就是說,想要存取一個鍵盤事件,你需要用以下的語法: ```cpp SDL_KeyboardEvent ke = event.key; //event的一個member, key, 是一個型別為SDL_KeyboardEvent的物件,我們宣告一個變數ke去儲存。 //其實可以不要額外多這步會比較好寫,這只是讓你了解你在幹嘛。 ``` 針對這個部分的敘述,其實有很大的錯誤,有興趣的人可以看這裡。沒興趣的人就當成”上面講的是對的!”這樣理解就行。 ### 取得按鍵種類 SDL有兩種取值的方式,分別叫”scancode”和”keycode”。兩種都用來決定到底是哪一個鍵被按下,比如說,希望按下鍵盤上的W鍵時,進行向前進的函數,則語法如下: ```cpp if(event.key.keysym.scancode == SDL_SCANCODE_Q){ MoveForward(); } //or, alternatively if(event.key.keysym.sym == SDLK_q){ MoveForward(); } ``` 關於按鍵對應的名稱,請查此表: [https://wiki.libsdl.org/SDL2/SDL_Keycode](https://wiki.libsdl.org/SDL2/SDL_Keycode) ### 取得按鍵修飾 如果我們希望偵測Ctrl+C,用以上的寫法會是: ```cpp if(event.key.keysym.sym == SDLK_LCTRL || event.key.keysym.sym == SDLK_RCTRL){ while(SDL_PollEvent(&event)){ if(event.key.keysym.sym == SDLK_c){ Copy(); } } } ``` 看起來程式碼就很複雜。此外,如果在按下Control後,都沒有按下C鍵,何時退出這個子While迴圈?(不要忘記主要程式結構就是兩個While了) 為了解決問題,SDL引入一種新的邏輯: **偵測C鍵的同時,檢查其他鍵有沒有被按下。**亦即將Ctrl當作一種鍵盤的修飾(modification)。如此一來就可以簡化程式碼。 ```cpp if(event.key.keysym.sym == SDLK_c && (event.key.keysym.mod == KMOD_CTRL)){ Copy(); } //event.key.keysym.mod: 按鍵模式 ``` 常用的KEYMOD如下: | Flags | 內容 | | --- | --- | | KMOD_NONE | 無特別Mod | | KMOD_SHIFT | 按下Shift鍵,可用KMOD_LSHIFT / KMOD_RSHIFT指定左右鍵。 | | KMOD_CTRL | 按下Ctrl鍵,可用KMOD_LCTRL/ KMOD_RCTRL指定左右鍵。 | | KMOD_ALT | 按下Alt鍵,可用KMOD_LALT/ KMOD_RALT指定左右鍵。 | | KMOD_GUI | 按下GUI鍵(Windows鍵/Command鍵),可用KMOD_LGUI/ KMOD_RGUI指定左右鍵。 | ### 取得按鍵狀態 ```cpp if(event.key.state == KEY_PRESSED){ //按下按鍵 } else if(event.key.state == KEY_RELEASED){ //放開按鍵 } ``` 鍵盤事件中只有這兩種狀態。 ### 時間戳 ```cpp Uint32 t = event.key.timestamp; ``` 回傳的時間是”自SDL library啟動之後的毫秒數”,在後續解釋時間的章節會再詳細解釋。 ## SDL_MouseButtonEvent: 滑鼠點按事件 存取滑鼠點按事件: ```cpp SDL_MouseButtonEvent me = event.button; //event的一個member, button, 是一個型別為SDL_MouseButtonEvent的物件,我們宣告一個變數me去儲存。 //其實可以不要額外多這步會比較好寫,這只是讓你了解你在幹嘛 ``` ### 取得點按位置 ```cpp Sint32 x = event.button.x; Sint32 y = event.button.y; ``` 單位同程式的其他部分,使用像素(px)作為單位,左上角為(0, 0),向左為+x,向下為+y。考慮到你可能點在視窗外,(x, y)可以帶有負值。 ### 取得按鍵種類 ```cpp if(event.button.button == SDL_BUTTON_LEFT){ //按下左鍵 } else if(event.button.button == SDL_BUTTON_RIGHT){ //按下右鍵 } ``` 使用這個參數可以辨別滑鼠被按下的是左鍵或右鍵。亦可以設定成偵測中鍵(MIDDLE)或輔助鍵(X1、X2)。 ### 連點偵測 ```cpp Uint8 ContinualClicks = event.button.clicks; ``` 這個參數會回傳滑鼠是否屬於連點狀態。若是單擊,則回傳1,雙擊,回傳2,依此類推。 --- 本章節介紹SDL2中的事件處理,包括事件種類、鍵盤事件、滑鼠點按事件等,並提供相關的程式碼範例。整個SDL2教學的基本盤就到此結束,你已經可以利用圖片和文字溝通,並透過監測使用者事件控制遊戲的進展了! 有了以上的技能,完成一個基本的,稱得上遊戲的程式絕對沒有問題。接下來,你可以深入學習對遊戲的控制,添加動畫、配樂、計時同步等等功能來增加遊戲性! 附錄大概講解了到此為止,我們把C裡面的各個東西稱為”物件”,所謂物件是如何透過純C實現的? --- ## 附錄: SDL_Event到底是個啥? 說到底,SDL的”物件”其實都不是C++意義上的物件。它們都是用C所有的功能組合而成的,只是行為模式相仿而已。 **SDL_Event其實是一個Union。**Union是一個很類似struct的東西,它們都用類似的語法。例如: ```cpp union num { int i; float f; double d; Uint32 u; }; //union的定義,跟struct類似。 num x; //宣告(instance creation) x.f = 3.2; //賦值 float y = x.f //取值 ``` 然而,它們有根本上的不同。你可以試著編譯下面的範例: ```cpp #include <iostream> using namespace std; union num{ int i; float f; double d; }; int main(){ num x; x.f = 3.2; cout << x.i << "\n" << x.f << "\n" << x.d; } ``` 你會先看到,x.i和x.d都沒有被賦值就呼叫了,這樣還可以輸出答案? 而且你每次都會得到一個相同的輸出: ```cpp 1078774989 3.2 5.32986e-315 ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully