RubB/SDL2 시작

66403 단어 Rubysdl2sdltech

개시하다


Ruby/SDL이 업데이트가 되지 않는다고 판단해 포기하면Ruby/SDL2 이미 공개되었으니 사용해 보세요.

설정


어쨌든 먼저 해~/src/ruby-sdl2-playground.
#!/bin/sh
cd ~/src
rm -fr ruby-sdl2-playground
mkdir -p ruby-sdl2-playground
cd ruby-sdl2-playground
bundle init
bundle add ruby-sdl2 --require sdl2 --optimistic
bundle exec ruby -r sdl2 -e "p SDL2"

cat <<'EOF' > main.rb
require "bundler/setup"
Bundler.require(:default)

SDL2.init(SDL2::INIT_EVERYTHING)
pos = SDL2::Window::POS_CENTERED
window = SDL2::Window.create("(title)", pos, pos, 640, 480, 0)
flags = 0
flags |= SDL2::Renderer::Flags::PRESENTVSYNC
renderer = window.create_renderer(-1, flags)

120.times do
  SDL2::Event.poll
  renderer.present
end
EOF

bundle exec ruby main.rb

  • 지정 SDL2::Renderer::Flags::PRESENTVSYNC 시 수직 동기화
  • 위의 조개 스크립트를 재생하면, 아래와 같이 2초의 윈도를 표시합니다.
    window

    진짜 2초?


    윈도는 SDL2::Window.create 시기에 시작된 것 같지 않다.
    우선 적립된 활동을 버리지 않으면SDL2::Event.poll 아무리 기다려도 작동하지 않는다.
    다음 이벤트를 하나씩 들려드리도록 하겠습니다.
  • SHOWN
  • EXPOSED
  • FOCUS_GAINED
  • 드디어 FOCUS_GAINED의 시간에 표시됩니다.
    환경에 따라 다를 수 있지만 내게는 일곱 번째 순환이 생겼다.
    따라서 2초짜리 윈도를 표시하려면 정확히 아래FOCUS_GAINED를 기다렸다가 해야 한다.
    하지만 아무도 그런 사소한 일에 참견하지 않겠지.
    catch :break do
      loop do
        while ev = SDL2::Event.poll
          case ev
          when SDL2::Event::Window
            case ev.event
            when SDL2::Event::Window::FOCUS_GAINED
              throw :break
            end
          end
        end
      end
    end
    
    sleep(2)
    

    뭘 눌러봐.


    require "bundler/setup"
    Bundler.require(:default)
    
    SDL2.init(SDL2::INIT_EVERYTHING)
    pos = SDL2::Window::POS_CENTERED
    window = SDL2::Window.create("(title)", pos, pos, 640, 480, 0)
    flags = 0
    flags |= SDL2::Renderer::Flags::ACCELERATED
    flags |= SDL2::Renderer::Flags::PRESENTVSYNC
    renderer = window.create_renderer(-1, flags)
    
    include Math
    
    frame_counter = 0
    loop do
      while ev = SDL2::Event.poll
        case ev
        when SDL2::Event::Quit
          exit
        when SDL2::Event::KeyDown
          case ev.scancode
          when SDL2::Key::Scan::ESCAPE
            exit
          when SDL2::Key::Scan::Q
            exit
          end
        end
      end
    
      renderer.draw_blend_mode = SDL2::BlendMode::BLEND
      renderer.draw_color = [0, 0, 64, 28]
      renderer.fill_rect(SDL2::Rect.new(0, 0, *window.size))
    
      renderer.draw_blend_mode = SDL2::BlendMode::NONE
      renderer.draw_color = [255, 255, 255]
    
      r = 64
      w, h = window.size
      x = w / 2 + cos(PI * frame_counter * 0.02 * 0.7) * w * 0.4
      y = h / 2 + sin(PI * frame_counter * 0.02 * 0.8) * h * 0.4
      renderer.fill_rect(SDL2::Rect.new(x - r, y - r, r * 2, r * 2))
    
      renderer.present
      frame_counter += 1
    end
    
  • SDL2::Renderer::Flags::ACCELERATED 아무래도 강한 것 같아서 넣어봤어
  • lissajous
    GIF로 하는 방법은 거칠지만 실제로는 매끄럽지 않다

    소형 프레임


    창고에 포함된 견본품은 위와 같이 많이 쓰여 있다.
    샘플로 끈적끈적한 글씨 쓰는 방식은 일직선으로 처리되어 이해하기 쉬우나 이렇게 코드를 넣으면 어떻게 된 일인지 모르겠다.
    그리고 매번 초기화 처리를 쓰고 특정 키로 주 순환 처리를 없애고 싶지 않다.
    따라서 템플릿 방법 모드에서 필요한 부분만 쓴다.
    require "bundler/setup"
    Bundler.require(:default)
    
    class Base
      include Math
    
      class << self
        def run(*args)
          new(*args).run
        end
      end
    
      def run
        setup
        loop do
          event_loop
          update
          before_view
          view
          after_view
        end
      end
    
      private
    
      def setup
        SDL2.init(SDL2::INIT_EVERYTHING)
      end
    
      def update
      end
    
      def view
      end
    
      def before_view
      end
    
      def after_view
      end
    
      def event_loop
        while ev = SDL2::Event.poll
          event_handle(ev)
        end
      end
    
      def event_handle(ev)
        case ev
        when SDL2::Event::Quit
          exit
        when SDL2::Event::KeyDown
          case ev.scancode
          when SDL2::Key::Scan::ESCAPE
            exit
          when SDL2::Key::Scan::Q
            exit
          end
        end
      end
    end
    
    # Base.run
    
    - 호출 순서와 최소한의 처리만 쓴다.
    추상적인 종류지만 윈도 응용 프로그램으로 실행할 수 없습니다.

    창 추가


    module WindowMethods
      attr_accessor :window
      attr_accessor :renderer
      attr_accessor :frame_counter
    
      def setup
        super
    
        flags = 0
        # flags |= SDL2::Window::Flags::FULLSCREEN
        # flags |= SDL2::Window::Flags::FULLSCREEN_DESKTOP
        pos = SDL2::Window::POS_CENTERED
        @window = SDL2::Window.create("(Title)", pos, pos, 640, 480, flags)
    
        flags = 0
        flags |= SDL2::Renderer::Flags::ACCELERATED
        flags |= SDL2::Renderer::Flags::PRESENTVSYNC
        @renderer = @window.create_renderer(-1, flags)
    
        @frame_counter = 0
      end
    
      def before_view
        super
    
        renderer.draw_blend_mode = SDL2::BlendMode::BLEND
        renderer.draw_color = [0, 0, 64, 28]
        renderer.fill_rect(SDL2::Rect.new(0, 0, *@window.size))
    
        renderer.draw_blend_mode = SDL2::BlendMode::NONE
        renderer.draw_color = [255, 255, 255]
      end
    
      def after_view
        super
    
        @frame_counter += 1
        renderer.present
      end
    end
    
    Base.prepend WindowMethods
    
    # Base.run
    
    실행 후 Window가 나타나면 OK
    마술 번호가 눈에 띄지만 그냥 놔둬.
    추상화하거나 고정화하고 싶지만 반대로 하지 않는 인내가 중요하고, 좋은 점이 엉망진창인 상태에서 하거나 안 하면 된다.

    완성


    class App < Base
      def view
        super
    
        r = 64
        w, h = window.size
        x = w / 2 + cos(PI * frame_counter * 0.02 * 0.7) * w * 0.4
        y = h / 2 + sin(PI * frame_counter * 0.02 * 0.8) * h * 0.4
        renderer.fill_rect(SDL2::Rect.new(x - r, y - r, r * 2, r * 2))
      end
    
      run
    end
    
    미니 프레임워크는 코드를 매우 간단하게 만든다.
    나는 단지 여기에 쓰고 싶을 뿐이다.

    작은 프레임 확장


    초수를 알고 싶어요.


    module FpsMethods
      attr_reader :fps
    
      def setup
        super
    
        @fps = 60
        @fps_counter = 0
        @old_ticks = SDL2.get_ticks
      end
    
      def update
        super
    
        @fps_counter += 1
        v = SDL2.get_ticks
        t = v - @old_ticks
        if t >= 1000
          @fps = @fps_counter
          @old_ticks = v
          @fps_counter = 0
          p fps
        end
      end
    end
    
    Base.prepend FpsMethods
    
    # Base.run
    
    분류 처리를 통해 위에서 말한 바와 같이 원시 코드를 변경하지 않고 기능을 추가할 수 있다.
    따라서 예를 들어 조종대의 상태를 읽고 쉽게 처리할 수 있는 형태로 만드는 등도 이렇게 모듈화하여 추가할 수 있다.
    한눈에 미니 프레임 안에서 잘 알려지지 않은 실례 변수가 퍼지는 상황이 좋지 않아 학급화하는 것이 좋을 것 같지만 그건 잠시 내버려두자.

    텍스트 보이기


    화면에 위에서 구한 초수 프레임을 표시합니다.
    require "pathname"
    
    module FontMethods
      def setup
        super
    
        font_file = "~/Library/Fonts/Ricty-Regular.ttf"
        font_size = 32
    
        SDL2::TTF.init
        @font = SDL2::TTF.open(Pathname(font_file).expand_path.to_s, font_size)
        @font.kerning = true
      end
    
      def after_view
        system_line "#{frame_counter} #{fps}fps"
    
        super
      end
    
      def system_line(text)
        rect = SDL2::Rect.new(0, 0, *@font.size_text(text))
    
        renderer.draw_blend_mode = SDL2::BlendMode::NONE
        renderer.draw_color = [0, 0, 128]
        renderer.fill_rect(rect)
    
        font_color = [255, 255, 255]
        texture = renderer.create_texture_from(@font.render_blended(text, font_color))
        renderer.copy(texture, nil, rect)
      end
    end
    
    Base.prepend FontMethods
    
    # Base.run
    
    텍스트만 보여도 이렇게 많이 써야 한다고 생각했는데 좋은 방법을 생각해 냈으면 좋겠어요.
    이렇게 하면 왼쪽 상단에 계수기와 초 사이의 프레임 수를 표시할 수 있다.
    font

    신경 써. 슈퍼밖에 없어.


    방법은 Rails의 before액션 같은 블록으로 바꾸는 게 좋을 것 같아서요.
    require "active_support/callbacks"
    require "active_support/core_ext/object/blank"
    
    class Foo
      include ActiveSupport::Callbacks
      define_callbacks :view
    
      def self.view(*args, &block)
        set_callback(:view, *args, &block)
      end
    
      def run
        run_callbacks :view
      end
    
      view do
        p 1
      end
    end
    
    class Bar < Foo
      view do
        p 2
      end
      view do
        p 3
      end
    end
    
    Bar.new.run
    # >> 1
    # >> 2
    # >> 3
    
    view의 정의가 추가되었습니다.
    하지만 슈퍼라고 부르지 않고 다시 쓰기를 선택할 여지가 없어 길고 짧았다.
    따라서 미니 프레임워크의 응용은 잠시 보류됩니다.

    벡터 라이브러리 삽입


    개별 계산 xy가 번거롭기 때문에 벡터 라이브러리에 넣습니다.
    어쨌든 gem search vector에 나오는 바이어스 2d를 사용해 보세요.
    shell
    bundle add vector2d
    
    이렇게 사용할 수 있을 것 같아요.
    vec = Vector2d(3, 4)   # => Vector2d(3,4)
    vec.x                  # => 3
    vec.y                  # => 4
    vec.length             # => 5.0
    vec * 2                # => Vector2d(6,8)
    vec * Vector2d(2, 2)   # => Vector2d(6,8)
    vec + Vector2d(1, 1)   # => Vector2d(4,5)
    vec.normalize.length   # => 1.0
    vec.to_a               # => [3, 4]
    
    여기 함정이 있어요.
    이 도서관은 매우 무겁다!
    나는 이런 수학과의 도서관은 특히 속도에 주의해서 실현된 것이라고 생각한다.
    이 때문에 이후 만들어진 시위 행진이 15FPS가 된 이유가 Vector2d라는 게 믿기지 않는다.
    Ruby의 표준 라이브러리이지만 언제 외부gem의 matrix(포함된 Vector 라이브러리)와 속도를 비교할지 모른다면--
    require "bundler/inline"
    
    gemfile do
      source "https://rubygems.org"
      gem "matrix"
      gem "vector2d"
      gem "benchmark-ips"
    end
    
    Benchmark.ips do |x|
      x.report("Vector")   { Vector[2, 3] + Vector[4, 5]     }
      x.report("Vector2d") { Vector2d(2, 3) + Vector2d(4, 5) }
      x.compare!
    end
    
    # Warming up --------------------------------------
    #               Vector    49.777k i/100ms
    #             Vector2d     1.702k i/100ms
    # Calculating -------------------------------------
    #               Vector    493.319k (± 1.8%) i/s -      2.489M in   5.046831s
    #             Vector2d     23.293k (±11.6%) i/s -    115.736k in   5.061193s
    #
    # Comparison:
    #               Vector:   493319.3 i/s
    #             Vector2d:    23293.3 i/s - 21.18x  (± 0.00) slower
    
    21.18배나 늦었네.
    뭘 하면 이렇게 늦을지 모르겠지만 창조적 인코딩은 안 어울린다는 걸 알아요.
    그럼 왜 처음부터 matrix를 사용하지 않았을까요? 안 좋은 데가 있어서요.
    하지만 좋은 것이 없으니 마릭스의 벡터류를 개량해 보자.
    방금 바이어스 2d를 끄고 마릭스에 넣으세요.
    shell
    bundle remove vector2d
    bundle add matrix
    
    class Vector2d < Vector
      def +(v)
        if v.kind_of?(self.class)
          super
        else
          super(self.class[v, v])
        end
      end
    
      def -(v)
        if v.kind_of?(self.class)
          super
        else
          super(self.class[v, v])
        end
      end
    
      def *(v)
        if v.kind_of?(self.class)
          map2(v) { |a, b| a * b }
        else
          super
        end
      end
    
      def /(v)
        if v.kind_of?(self.class)
          map2(v) { |a, b| a / b }
        else
          super
        end
      end
    end
    
    def Vector2d(*args)
      Vector2d[*args]
    end
    
    완료.위와 같이 중량을 초과했기 때문에 다음과 같은 내용을 쓸 수 있습니다.
    Vector2d(2, 3) + 1              # => Vector[3, 4]
    Vector2d(2, 3) - 1              # => Vector[1, 2]
    Vector2d(2, 3) * Vector2d(2, 3) # => Vector[4, 9]
    Vector2d(2, 3) / Vector2d(2, 3) # => Vector[1, 1]
    
    이렇게 하면 사용하기 쉽지만matrix의 벡터 라이브러리는 기능이 우선적인 다차원 대응판이기 때문에 속도적으로 창조적인 인코딩에 적합하지 않다.
    이번에는 matrix 버전을 사용하더라도 앞으로는 2D 전용 심플한 버전으로 바꾸는 것이 좋다.
    만약 실행을 시도해 본다면--
    require "matrix"
    require "benchmark/ips"
    
    class MyVector
      attr_accessor :x, :y
    
      def initialize(x, y)
        @x, @y = x, y
      end
    
      def +(other)
        self.class.new(@x + other.x, @y * other.y)
      end
    end
    
    Benchmark.ips do |x|
      x.report("Vector")   { Vector[1, 2] + Vector[3, 4]             }
      x.report("MyVector") { MyVector.new(1, 2) + MyVector.new(3, 4) }
      x.compare!
    end
    
    # Warming up --------------------------------------
    #               Vector    59.187k i/100ms
    #             MyVector   138.991k i/100ms
    # Calculating -------------------------------------
    #               Vector    588.342k (±10.0%) i/s -      2.900M in   5.000701s
    #             MyVector      1.371M (± 4.3%) i/s -      6.950M in   5.078438s
    #
    # Comparison:
    #             MyVector:  1371388.1 i/s
    #               Vector:   588342.3 i/s - 2.33x  (± 0.00) slower
    
    속도가 2.3배 높아졌다.
    요점
  • vector 2d는 믿을 수 없을 정도로 느리다
  • matrix는vector2d보다 21.18배 빠르다
  • 자체 제작 2D 전용이면 matrix보다 2.3배
  • 빠름

    Tixy 클론의 기본을 만들어 보십시오.


    Tixy(Creator:)는 짧은 사이즈로 다양한 디자인을 시도하는 쿨한 사이트입니다.
    존중하는 마음으로 이걸 흉내내다.
    구체적으로 말하면
  • Time
  • 인덱스(단지 번호)
  • x 좌표
  • y 좌표
  • 받았어요. - 1.0...1.0 값에 따라 셀의 색상과 크기를 변경합니다.
    class TixyCloneApp < Base
      CELL_N = 16
    
      def setup
        super
    
        @window_rect    = Vector2d(*window.size)
        @cell_wh        = @window_rect * 1.0 / CELL_N
        @inner_top_left = @window_rect * 0.5 - @cell_wh * CELL_N * 0.5
      end
    
      def before_view
        renderer.draw_color = [0, 0, 0]
        renderer.clear
      end
    
      def view
        super
    
        time = SDL2.get_ticks.fdiv(1000)
        index = 0
        CELL_N.times do |y|
          CELL_N.times do |x|
            r = tixy_func(time, index, x, y)
            if r.nonzero?
              r = r.clamp(-1.0, 1.0)
              center = @inner_top_left + @cell_wh * Vector2d(x, y) + @cell_wh * 0.5
              radius = @cell_wh * 0.5 * r.abs * 0.95
              top_left = center - radius
              renderer.draw_color = tixy_color(r)
              renderer.fill_rect(SDL2::Rect.new(*top_left, *(radius * 2)))
            end
            index += 1
          end
        end
      end
    
      def tixy_func(t, i, x, y)
        sin(t - sqrt((x - 7.5)**2 + (y - 6)**2))
      end
    
      def tixy_color(v)
        if v.positive?
          v = 1.0
        else
          v = -1.0
        end
        c = v.abs * 255
        if v.positive?
          [c, c, c]
        else
          [c, 0, 0]
        end
      end
    
      run
    end
    
    tixy_clone_base
  • tixy_func 함수를 바꾸는 내용은 각종 도안을 나타낼 수 있다
  • 16*16=256개의 사각형을 그려도 60FPS에서 천천히 이동
  • 사용 후의 느낌

  • Window 모드에서 수직으로 동기화할 수 있어 기쁩니다.
  • Ruby/SDL인 경우 왠지 전체 화면에서만 수직으로 동기화할 수 있음
  • 루비 지령으로 움직일 수 있어서 좋아요.
  • 루비 대신 rsdl로 시작하지 않으면 루비가 작동하지 않습니다
  • 시동만 걸면 고맙다지만 이 함정에 자주 빠진다
  • SGE 없이도 그릴 수 있어서 좋았어요.
  • 루비/SDL 시대에 SGE를 따로 넣지 않으면 잘 묘사할 수 없다
  • 이 SGE의 설치는 매우 치열하다
  • 어디서 다운로드하는 게 좋을지 모르겠어요.
  • 아직 소식이 없음
  • 수수께끼 같은 패치도 해야 한다
  • 이 때문에 Ruby/SDL 호스트가 몰래 묶인 것 같습니다
  • 구축 옵션 설치 후 함께 설치
  • 사용하기 편한 것 같아요.
  • draw_색상으로 색상을 지정할 수 있는 곳
  • PRESENTVSYNC를 통해서만 수직 동기화 또는
  • 빨라진 것 같아.
  • 루비/SDL 처리 누락된 묘사수도 60fps
  • 를 유지하고 있다
  • 현재 일본어 문서 없음

  • 창고 샘플

  • rubysdl2_init_video

  • SDL2 문서
  • SDL2로 곤두박질
  • gg력이 중요하다
  • 기타 귀속 문서도 대체적으로 같을 수 있다
  • Nannou의 서 있는 위치와 크게 다르다
  • Nannou
  • 프레임
  • 종료 또는 전체 화면화된 버튼 귀속 설정 완료
  • 창조적 인코딩을 즉시 시작할 수 있음
  • 벡터 라이브러리가 엉망진창으로 만들어졌다
  • 처리 영역의 클래스도 만들었다
  • 어디에 무엇을 쓸지 엄격히 결정
  • Ruby/SDL
  • 래치
  • 모두 사용자 제공
  • Escape 키를 눌러도 끝나지 않습니다.
  • 메인 순환 직접 쓰기
  • 장난 반으로 전체 화면을 전체 화면으로 설정하면 고장 발생 시 PC를 낮추지 않으면 복구할 수 없음
  • FPS 값을 즉시 알 수 있는 방법이 없음
  • 벡터 라이브러리 없음
  • 처리 구역의 편의류가 없음
  • Rect가 있지만 x, y, w, h 정도의 역할 분배를 거의 유지한다
  • 어디서 무엇을 쓸지 아직 결정하지 못했다
  • 그게 좋아요
  • 인용하다


    https://github.com/ohai/ruby-sdl2
    http://ohai.github.io/ruby-sdl2/doc-en/

    좋은 웹페이지 즐겨찾기