2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NimでSDL2 ⑥ 文字列を描画する

Posted at

#はじめに
NimでSDL2 ① 準備とウィンドウ表示
NimでSDL2 ② 画像を表示
NimでSDL2 ③ 入力処理
NimでSDL2 ④ FPS(frames per second)の調整
NimでSDL2 ⑤ 音を出す

文字列を描画します。

#概要
まずはttfを初期化。
でttfファイルを読み込んで、FontPtrを作成。
そのFontPtrから文字列を指定してサーフェイスを作成。
そのサーフェイスをテクスチャーに変換して描画する。(ちとめんどい)

コード

追記箇所はコメント参照

# sdl2/ttfをimport
import sdl2, sdl2/image, os, sugar, sets, sdl2/mixer, sdl2/ttf

template sdlFailIf(cond: typed, desc: string) =
  if cond: raise Exception.newException(
    desc & ": " & $getError())

type FPS* = ref object
  frameTime, targetFramePeriod: uint32

proc newFPS*(fps: int): FPS =
  FPS(targetFramePeriod: uint32(1000 / fps))

proc adjust*(self: FPS) =
  let now = getTicks()
  if self.frameTime > now:
    delay(self.frameTime - now)
  self.frameTime += self.targetFramePeriod

type SDL2Context = ref object
  window: WindowPtr
  renderer: RendererPtr
  cleanups: seq[proc ()]
  pressed: HashSet[cint]
  press: HashSet[cint]
  fps: FPS

proc cleanup(self: SDL2Context) =
  while self.cleanups.len > 0:
    (self.cleanups.pop)()

proc init(self: SDL2Context, title: string, w, h: cint): SDL2Context {.discardable.} =
  result = self
  sdlFailIf(not sdl2.init(INIT_VIDEO), "SDL2 init")
  self.cleanups.add(() => sdl2.quit())
  self.window = createWindow(title = title,
                            x = SDL_WINDOWPOS_CENTERED,
                            y = SDL_WINDOWPOS_CENTERED,
                            w = w, h = h,
                            flags = SDL_WINDOW_SHOWN)
  sdlFailIf(self.window.isNil, "createWindow")
  self.cleanups.add(() => self.window.destroy)
  sdlFailIf(image.init(IMG_INIT_PNG) != IMG_INIT_PNG, "image init")
  self.cleanups.add(() => image.quit())
  self.renderer = self.window.createRenderer(
    index = -1,
    flags = Renderer_Accelerated or Renderer_PresentVsync)
  sdlFailIf(self.renderer.isNil, "createRenderer")
  self.cleanups.add(() => self.renderer.destroy)
  self.renderer.setDrawColor(r=0, g=128, b=128)
  self.pressed = initHashSet[cint]()
  self.fps = newFPS(60)
  sdlFailIf(mixer.openAudio(
    MIX_DEFAULT_FREQUENCY,
    MIX_DEFAULT_FORMAT,
    MIX_DEFAULT_CHANNELS,
    4096) != 0, "openAudio")
  self.cleanups.add(() => closeAudio())
  # ttf初期化
  sdlFailif(ttfInit() == SdlError, "ttfInit")
  # クリーンアップ処理追加
  self.cleanups.add(() => ttfQuit())

proc loadTexture(self: SDL2Context, path: string): TexturePtr =
  result = self.renderer.loadTexture(path)
  sdlFailIf(result.isNil, "loadTexture")

proc keyStateUpdate*(self: SDL2Context, event: KeyboardEventPtr) =
  case event.kind
  of KeyDown:
    self.pressed.incl(event.keysym.sym)
    self.press.incl(event.keysym.sym)
  of KeyUp: self.pressed.excl(event.keysym.sym)
  else: discard

proc pollEvent(self: SDL2Context): bool =
  result = true
  var event = defaultEvent
  self.press.clear
  while pollEvent(event):
    case event.kind:
    of QuitEvent: return false
    of KeyDown, KeyUp: self.keyStateUpdate(event.key)
    else: discard

proc mainLoop(self: SDL2Context) =
  let
    w: cint = 256
    h: cint = 256
    speed: cint = 3
    neko = self.loadTexture("genbaneko.png")
  var
    windowWidth: cint
    windowHeight: cint
  let music = mixer.loadMUS("bgm.ogg")
  sdlFailIf(music.isNil, "loadMUS")
  let sound = mixer.loadWav("sound.wav")
  sdlFailIf(sound.isNil, "loadWav")
  self.window.getSize(windowWidth, windowHeight)
  var dst = rect(windowWidth div 2 - w div 2, windowHeight div 2 - h div 2, w, h)
  # ttfフォント読み込み
  let font = openFont("./mplus-1p-regular.ttf", 48)
  # エラーチェック
  sdlFailIf(font.isNil, "openFont")
  let
    # ttfフォントから文字列のサーフェイスを作成
    strSurface = font.renderUtf8Blended("ご安全に!!", color(255, 0, 0, 0))
    # サーフェイスをテクスチャーに変更
    strTexture = self.renderer.createTextureFromSurface(strSurface)
  # サーフェイスの描画先Rectを作成
  var strRect = rect(windowWidth div 2 - strSurface.w div 2, dst.y + dst.h - strSurface.h,
                     strSurface.w, strSurface.h)
  while self.pollEvent:
    if K_RIGHT in self.pressed: dst.x += speed
    if K_LEFT  in self.pressed: dst.x -= speed
    if K_DOWN  in self.pressed: dst.y += speed
    if K_UP    in self.pressed: dst.y -= speed
    if K_m in self.press:
      if playingMusic() == 0:
        sdlFailif(playMusic(music, 0) == -1, "playMusic")
      else:
        sdlFailif(haltMusic() == -1, "haltMusic")
    if K_s in self.press:
      sdlFailif(playChannel(-1, sound, 0) == -1, "playChannel")
    self.renderer.clear
    self.renderer.copyEx(neko, nil, dst.addr, 0.0, nil, SDL_FLIP_NONE)
    # 文字列のテクスチャーを描画
    self.renderer.copyEx(strTexture, nil, strRect.addr, 0.0, nil, SDL_FLIP_NONE)
    self.renderer.present
    self.fps.adjust

proc main() =
  let sdl2 = SDL2Context()
  defer: sdl2.cleanup
  sdl2.init("SDL2 Font sample", 640, 480).mainLoop

main()

結果

スクリーンショット 2020-12-26 13.57.03.png

終わりに

SDL2の基本的な機能はできたと感じ。
これまでのコードを参考に色々やってもらえればと〜。

以上〜。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?