RubB/SDL2 시작
개시하다
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초?
윈도는
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
아무래도 강한 것 같아서 넣어봤어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
텍스트만 보여도 이렇게 많이 써야 한다고 생각했는데 좋은 방법을 생각해 냈으면 좋겠어요.이렇게 하면 왼쪽 상단에 계수기와 초 사이의 프레임 수를 표시할 수 있다.
신경 써. 슈퍼밖에 없어.
방법은 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배 높아졌다.요점
Tixy 클론의 기본을 만들어 보십시오.
Tixy(Creator:)는 짧은 사이즈로 다양한 디자인을 시도하는 쿨한 사이트입니다.
존중하는 마음으로 이걸 흉내내다.
구체적으로 말하면
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_func
함수를 바꾸는 내용은 각종 도안을 나타낼 수 있다사용 후의 느낌
창고 샘플
rubysdl2_init_video
SDL2 문서
인용하다
Reference
이 문제에 관하여(RubB/SDL2 시작), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/megeton/articles/7170a5dc60c97b텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)