Skip to content

Instantly share code, notes, and snippets.

@mirichi
Created September 22, 2015 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mirichi/2aa0a9b058cc796f0a7e to your computer and use it in GitHub Desktop.
Save mirichi/2aa0a9b058cc796f0a7e to your computer and use it in GitHub Desktop.
sdl2rでDXRuby互換ぽいライブラリを作る(8)
require 'sdl2r'
require_relative 'fpstimer'
SDL.init(SDL::INIT_EVERYTHING)
SDL::TTF.init
module DXRuby
C_WHITE = [255, 255, 255]
C_RED = [255, 0, 0]
module Window
@_width = 640
@_height = 480
@_window = SDL.create_window("dxsdl2r Sample Application",
SDL::WINDOWPOS_UNDEFINED,
SDL::WINDOWPOS_UNDEFINED,
@_width,
@_height,
SDL::WINDOW_HIDDEN)
@_renderer = SDL.create_renderer(@_window, -1, 0)
# 描画予約配列
@_reservation = []
def self.width;@_width;end
def self.height;@_height;end
def self._window;@_window;end
def self._renderer;@_renderer;end
def self.width=(v);@_width=v;end
def self.height=(v);@_height=v;end
def self.draw_box_fill(x1, y1, x2, y2, color, z=0)
@_render_target.draw(x1, y1, x2, y2, color, z)
end
def self.draw(x, y, image, z=0)
@_render_target.draw(x, y, image, z)
end
def self.loop
timer = FPSTimer.instance
timer.reset
SDL.set_window_size(@_window, @_width, @_height)
SDL.show_window(@_window)
Kernel.loop do
timer.wait_frame do
return if Input.update
yield
@_render_target.update
SDL.render_present(@_renderer)
end
end
end
def self.fps=(v)
FPSTimer.instance.fps = v
end
def self.real_fps
FPSTimer.instance.real_fps
end
def self.caption
SDL.get_window_title(@_window)
end
def self.caption=(str)
SDL.set_window_title(@_window, str)
str
end
def bgcolor
@_render_target.bgcolor
end
def bgcolor=(bgcolor)
@_render_target.bgcolor = bgcolor
end
end
module Input
# マウスの情報
@_mouse_button = @_mouse_x = @_mouse_y = 0
@_old_mouse_button = @_old_mouse_x = @_old_mouse_y = 0
# キーボードの情報
@_keys = []
@_old_keys = []
# 内部情報の公開
def self._mouse_button;@_mouse_button;end
def self._old_mouse_button;@_old_mouse_button;end
def self._keys;@_keys;end
def self._old_keys;@_old_keys;end
# マウスボタン判定用クラス
class MouseButton
def initialize(b) # 1が左、2が真ん中、3が右
@_button = b
end
def down?
(SDL::BUTTON(@_button) & Input._mouse_button) != 0
end
def push?
(SDL::BUTTON(@_button) & Input._mouse_button) != 0 and
(SDL::BUTTON(@_button) & Input._old_mouse_button) == 0
end
def release?
(SDL::BUTTON(@_button) & Input._mouse_button) == 0 and
(SDL::BUTTON(@_button) & Input._old_mouse_button) != 0
end
end
# キーボード判定用クラス
class Keyboard
def initialize(k)
@_key = k
end
def down?
Input._keys[@_key]
end
def push?
Input._keys[@_key] and !Input._old_keys[@_key]
end
def release?
!Input._keys[@_key] and Input._old_keys[@_key]
end
end
# 各種判定メソッド
# 一応DXRuby互換で複数用意しているが中身はどれも同じ
def self.push?(button);button.push?;end
def self.down?(button);button.down?;end
def self.release?(button);button.release?;end
def self.mouse_push?(button);button.push?;end
def self.mouse_down?(button);button.down?;end
def self.mouse_release?(button);button.release?;end
def self.key_push?(button);button.push?;end
def self.key_down?(button);button.down?;end
def self.key_release?(button);button.release?;end
def self.mouse_x
@_mouse_x
end
def self.mouse_y
@_mouse_y
end
def self.x
x = 0
x -= 1 if K_LEFT.down?
x += 1 if K_RIGHT.down?
x
end
def self.y
y = 0
y -= 1 if K_UP.down?
y += 1 if K_DOWN.down?
y
end
def self.update
# 押されているキー一覧を取得する
@_old_keys = @_keys
@_keys = SDL.get_keyboard_state
# マウスの状態を取得する
@_old_mouse_button, @_old_mouse_x, @_old_mouse_y = @_mouse_button, @_mouse_x, @_mouse_y
@_mouse_button, @_mouse_x, @_mouse_y = SDL.get_mouse_state
# SDL2のイベント処理
while event = SDL.poll_event do
case event.type
when SDL::QUIT
return true
end
end
false
end
end
# ボタン定数
M_LBUTTON = Input::MouseButton.new(1)
M_MBUTTON = Input::MouseButton.new(2)
M_RBUTTON = Input::MouseButton.new(3)
K_LEFT = Input::Keyboard.new(SDL::SCANCODE_LEFT)
K_RIGHT = Input::Keyboard.new(SDL::SCANCODE_RIGHT)
K_UP = Input::Keyboard.new(SDL::SCANCODE_UP)
K_DOWN = Input::Keyboard.new(SDL::SCANCODE_DOWN)
K_SPACE = Input::Keyboard.new(SDL::SCANCODE_SPACE)
K_ESCAPE = Input::Keyboard.new(SDL::SCANCODE_ESCAPE)
K_Z = Input::Keyboard.new(SDL::SCANCODE_Z)
K_X = Input::Keyboard.new(SDL::SCANCODE_X)
K_C = Input::Keyboard.new(SDL::SCANCODE_C)
K_LSHIFT = Input::Keyboard.new(SDL::SCANCODE_LSHIFT)
K_RSHIFT = Input::Keyboard.new(SDL::SCANCODE_RSHIFT)
class Image
attr_accessor :_surface, :_texture, :_pixels
def self.load(filename)
image = Image.new(0, 0)
image._surface = SDL::IMG.load(filename)
image._pixels = image._surface.pixels
image
end
def self.load_tiles(filename, cx, cy)
surface = SDL::IMG.load(filename)
w, h = surface.w, surface.h
ary = []
cy.times do |y|
cx.times do |x|
tmp = Image.new(0, 0)
# IntelCPUはリトルエンディアンだがビッグエンディアンにも一応対応しておく
# 画像フォーマットは32bit固定
if SDL::BYTEORDER == SDL::BIG_ENDIAN
tmp._surface = SDL.create_rgb_surface(0, w / cx, h / cy, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff)
else
tmp._surface = SDL.create_rgb_surface(0, w / cx, h / cy, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)
end
SDL.blit_surface(surface, SDL::Rect.new(w / cx * x, h / cy * y, w / cx, h / cy), tmp._surface, nil)
tmp._pixels = tmp._surface.pixels
ary << tmp
end
end
ary
end
def initialize(w, h, color=[0, 0, 0, 0])
# wとhの両方が0の場合はSurfaceを生成しない
return if w == 0 and h == 0
# IntelCPUはリトルエンディアンだがビッグエンディアンにも一応対応しておく
# 画像フォーマットは32bit固定
if SDL::BYTEORDER == SDL::BIG_ENDIAN
@_surface = SDL.create_rgb_surface(0, w, h, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff)
else
@_surface = SDL.create_rgb_surface(0, w, h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)
end
# 指定色で塗りつぶす
SDL.fill_rect(@_surface, nil, DXRuby._convert_color_dxruby_to_sdl(color))
# Pixelsオブジェクト取得
@_pixels = @_surface.pixels
end
def width
@_surface.w
end
def height
@_surface.h
end
# テクスチャを破棄する
# 次に描画で使われる際に再生成される
def _modify
if @_texture
SDL.destroy_texture(@_texture)
@_texture = nil
end
nil
end
# テクスチャ生成
def _create_texture
# テクスチャ生成
@_texture = SDL.create_texture_from_surface(Window._renderer, @_surface)
end
# ピクセルに色を置く
def []=(x, y, color)
tmp = @_pixels[x, y] = DXRuby._convert_color_dxruby_to_sdl(color)
self._modify
tmp
end
# ピクセルの色を取得する
def [](x, y)
tmp = @_pixels[x, y]
self._modify
DXRuby._convert_color_sdl_to_dxruby(tmp)
end
end
class RenderTarget
attr_accessor :_texture
def initialize(w, h, bgcolor=[0, 0, 0, 0])
@_reservation = []
@_bgcolor = DXRuby._convert_color_dxruby_to_sdl(bgcolor)
return if w == 0 and h == 0
@_texture = SDL.create_texture(Window._renderer, SDL::PIXELFORMAT_RGBA8888, SDL::TEXTUREACCESS_TARGET, w, h)
end
def draw_box_fill(x1, y1, x2, y2, color, z=0)
tmp = DXRuby._convert_color_dxruby_to_sdl(color)
prc = ->{
SDL.set_render_draw_blend_mode(@_renderer, SDL::BLENDMODE_BLEND)
SDL.set_render_draw_color(@_renderer, *tmp)
SDL.render_fill_rect(@_renderer, SDL::Rect.new(x1, y1, x2 - x1 + 1, y2 - y1 + 1))
}
@_reservation << [z, prc]
end
def draw(x, y, image, z=0)
prc = ->{
image._create_texture unless image._texture
SDL.set_texture_blend_mode(image._texture, SDL::BLENDMODE_BLEND)
SDL.render_copy(Window._renderer, image._texture, nil, SDL::Rect.new(x, y, image.width, image.height))
}
@_reservation << [z, prc]
end
def draw_font(x, y, str, font, hash = {})
option = {
color: [255, 255, 255, 255],
z: 0,
}.merge(hash)
sur = SDL::TTF.render_utf8_blended(font._font, str, DXRuby._convert_color_dxruby_to_sdl(option[:color]))
tex = SDL.create_texture_from_surface(Window._renderer, sur)
SDL.free_surface(sur)
_, _, w, h = SDL.query_texture(tex)
prc = ->{
SDL.set_texture_blend_mode(tex, SDL::BLENDMODE_BLEND)
SDL.render_copy(Window._renderer, tex, nil, SDL::Rect.new(x, y, w, h))
}
@_reservation << [option[:z], prc]
end
def clear
SDL.set_render_target(Window._renderer, @_texture)
SDL.set_render_draw_color(Window._renderer, *@_bgcolor)
SDL.set_render_draw_blend_mode(Window._renderer, SDL::BLENDMODE_NONE)
SDL.render_fill_rect(Window._renderer, nil)
end
def update
self.clear
@_reservation.sort_by!{|v|v[0]}.each{|v|v[1].call}
@_reservation.clear
end
def width
SDL.query_texture(@_texture)[2]
end
def height
SDL.query_texture(@_texture)[3]
end
def bgcolor
DXRuby._convert_color_sdl_to_dxruby(@_bgcolor)
end
def bgcolor=(bgcolor)
@_bgcolor = DXRuby._convert_color_dxruby_to_sdl(bgcolor)
end
end
class Font
attr_accessor :_font
# {ファイル名=>{フェイスのファミリー名=>index}}というハッシュで情報を保持する
@@_fonts = {}
def self.install(filename)
tmp = {}
result = []
# 情報を得るためにとりあえずopenしてみる
font = SDL::TTF.open_font(filename, 24)
name = SDL::TTF.font_face_family_name(font)
tmp[name] = 0
result << name
SDL::TTF.close_font(font)
# 複数フェイスある場合は全部openしてみる
(1...SDL::TTF.font_faces(font)).each do |i|
font = SDL::TTF.open_font_index(filename, 24, i)
name = SDL::TTF.font_face_family_name(font)
tmp[name] = i
result << name
SDL::TTF.close_font(font)
end
@@_fonts[filename] = tmp
result
end
def initialize(size, fontname="IPAGothic")
@@_fonts.each do |filename, hash|
if hash.include?(fontname)
@_font = SDL::TTF.open_font_index(filename, size, hash[fontname])
break
end
end
raise unless @_font
end
self.install("./font/ipag.ttf")
@_default_font = Font.new(24)
def self.default
@_default_font
end
end
# DXRuby色配列からSDL::Color色配列へ変換
def self._convert_color_dxruby_to_sdl(color)
if color.size == 4
color[1..3] << color[0]
else
color + [255]
end
end
def self.convert_color_sdl_to_dxruby(color)
color[3] + color[0..2]
end
module Window
@_render_target = RenderTarget.new(0, 0, [0, 0, 0])
end
end
include DXRuby
END{
SDL::TTF.quit
SDL.quit
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment