前言

我们现在已经会显示图片了, 这一节我们来讲一下事件驱动

原理

SDL 事件包括了键盘输入, 鼠标输入, 手柄, 等等
SDL 会将所有发生的事件放到一个队列中去, 用户通过 sdl.PollEvent 来一个个获取, 直到队列为空

退出事件

 running := true
 for running {
     for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
         switch event.(type) {
         case *sdl.QuitEvent:
             println("Quit")
             running = false
             break
         }
     }
     sdl.Delay(16)
 }
  1. 之前的代码里最后都有这么一段
  2. for running 作为主循环, running 为 true 时, 循环会一直进行下去
  3. 在第一层循环里, 再嵌套一个循环不断调用 sdl.PollEvent 来获取事件来处理, 直到处理完所有事件退出这一层循环
  4. switch 可以通过 *sdl.QuitEvent 来判断事件的类型, 这里判断是否是 quit 事件, 如果是将 running 置为 false, 主循环退出
  5. sdl.Delay 的作用是做一个延迟, 每16豪秒做一次循环, 近似1秒60帧

完整的键盘事件

package main

import (
    "fmt"
    "github.com/veandco/go-sdl2/img"
    "github.com/veandco/go-sdl2/sdl"
)

const (
    KEY_PRESS_SURFACE_DEFAULT = iota
    KEY_PRESS_SURFACE_UP
    KEY_PRESS_SURFACE_DOWN
    KEY_PRESS_SURFACE_LEFT
    KEY_PRESS_SURFACE_RIGHT
    KEY_PRESS_SURFACE_TOTAL
)

var pressSurface [KEY_PRESS_SURFACE_TOTAL]*sdl.Surface

func init() {
    pressSurface[KEY_PRESS_SURFACE_DEFAULT] = loadSurface("../asserts/image/press.bmp")
    pressSurface[KEY_PRESS_SURFACE_UP] = loadSurface("../asserts/image/up.bmp")
    pressSurface[KEY_PRESS_SURFACE_DOWN] = loadSurface("../asserts/image/down.bmp")
    pressSurface[KEY_PRESS_SURFACE_RIGHT] = loadSurface("../asserts/image/right.bmp")
    pressSurface[KEY_PRESS_SURFACE_LEFT] = loadSurface("../asserts/image/left.bmp")
}

func loadSurface(path string) *sdl.Surface {
    img, err := img.Load(path)
    if err != nil {
        panic(err)
    }
    return img
}

func main() {
    sdl.Init(sdl.INIT_EVERYTHING)
    defer sdl.Quit()

    window, _ := sdl.CreateWindow("test", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
        670, 620, sdl.WINDOW_SHOWN)
    defer window.Destroy()

    surface, _ := window.GetSurface()
    defer surface.Free()
    surface.FillRect(nil, sdl.MapRGB(surface.Format, 0x0, 0x0, 0x0))

    curSurface := pressSurface[KEY_PRESS_SURFACE_DEFAULT]
    curSurface.BlitScaled(nil, surface, &sdl.Rect{X: 0, Y: 0, W: 52, H: 52})

    window.UpdateSurface()

    running := true
    for running {
        for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
            switch t := event.(type) {
            case *sdl.QuitEvent:
                println("Quit")
                running = false
                break
            case *sdl.KeyboardEvent:
                switch t.Keysym.Sym {
                case sdl.K_UP:
                    curSurface = pressSurface[KEY_PRESS_SURFACE_UP]
                case sdl.K_DOWN:
                    curSurface = pressSurface[KEY_PRESS_SURFACE_DOWN]
                case sdl.K_LEFT:
                    curSurface = pressSurface[KEY_PRESS_SURFACE_LEFT]
                case sdl.K_RIGHT:
                    curSurface = pressSurface[KEY_PRESS_SURFACE_RIGHT]
                }
            }
        }
        curSurface.Blit(nil, surface, nil)
        window.UpdateSurface()
        sdl.Delay(16)
    }
    stop()
}

func stop() {
    fmt.Println("game stop")
    for i := 0; i < KEY_PRESS_SURFACE_TOTAL; i++ {
        pressSurface[i].Free()
    }
}
  1. 该段代码的作用是打开窗口显示一张默认的图片, 然后用户可以按上下左右键, 按不同的键, 会显示不同的图片
  2. 初始的 const 定义了6个枚举值, 很容易理解
  3. pressSurface 是一个指针数组, 大小为5, 存放了5个 surface 指针, 分别指向了默认的 surface, 和上下左右这四个 surface
  4. init 函数中将五张图片加载进来并赋值给 pressSurface
  5. loadSurface 函数封装了一下 img.Load 函数, 很容易理解
  6. main 函数中 window, surface, 都是讲过的, curSurface 我们把它指向 pressSurface 数组中的第0个, 也就是默认 surface
  7. 主循环 for 中进行事件的检测, QuitEvent 是之前讲过的退出事件, KeyboardEvent 是键盘事件
  8. 当检测到是 KeyboardEvent 事件的时候, 还要嵌套一层 switch 来检测 t.Keysym.Sym 到底是哪个键被按下
  9. 根据不同的按键, 将 curSurface 指向 pressSurface 数组中相应的 surface
  10. 调用 curSurface.Blit 来将图片显示出来
  11. 最后还有一个 stop 函数, 在退出的时候记得将 surface 内存释放掉

源码 https://gitee.com/fcsvr/tank.git