class Smalruby::Character

キャラクターを表現するクラス

Attributes

checking_hit_targets[RW]
costume_index[RW]
costumes[RW]
enable_pen[R]
event_handlers[RW]
pen_color[RW]
rotation_style[R]
threads[RW]
volume[RW]

Public Class Methods

new(option = {}) click to toggle source
Calls superclass method
# File lib/smalruby/character.rb, line 31
def initialize(option = {})
  defaults = {
    x: 0,
    y: 0,
    costume: nil,
    costume_index: 0,
    angle: 0,
    visible: true,
    rotation_style: :free
  }
  opt = process_optional_arguments(option, defaults)

  @costume_name__index = {}
  @costumes = Array.wrap(opt[:costume]).compact.map.with_index { |costume, i|
    if costume.is_a?(String)
      md = /^(?:([^:]+):)?(.*)$/.match(costume)
      name = md[1]
      path = md[2]
      costume = Image.load(asset_path(path))
    end
    name ||= "costume#{i + 1}"
    @costume_name__index[name] = i
    costume
  }
  @costume_index = opt[:costume_index]
  super(opt[:x], opt[:y], @costumes[@costume_index])

  @event_handlers = {}
  @threads = []
  @checking_hit_targets = []
  @angle = 0 unless Util.windows?
  @enable_pen = false
  @pen_color = 'black'
  @volume = 100

  self.scale_x = 1.0
  self.scale_y = 1.0
  @vector = { x: 1, y: 0 }

  [:visible].each do |k|
    if opt.key?(k)
      send("#{k}=", opt[k])
    end
  end

  self.rotation_style = opt[:rotation_style]

  self.angle = opt[:angle] if opt[:angle] != 0

  # HACK: Windows XP SP3の環境でワーカースレッドで音声を読み込めな
  # い不具合が発生した。このためメインスレッドでプリロードしておく。
  %w(do re mi fa so ra si do_2).each do |n|
    new_sound("piano_#{n}.wav")
  end

  World.instance.objects << self
end

Public Instance Methods

alive?() click to toggle source
# File lib/smalruby/character.rb, line 554
def alive?
  @threads.compact!
  @threads.delete_if { |t|
    if t.alive?
      false
    else
      begin
        t.join
      rescue => e
        Util.print_exception(e)
        exit(1)
      end
      true
    end
  }
  @threads.length > 0
end
angle() click to toggle source

角度

Calls superclass method
# File lib/smalruby/character.rb, line 192
def angle
  return super if @rotation_style == :free

  x, y = @vector[:x], @vector[:y]
  a = Math.acos(x / Math.sqrt(x**2 + y**2)) * 180 / Math::PI
  a = 360 - a if y < 0
  a
end
angle=(val) click to toggle source

( )度に向ける

Calls superclass method
# File lib/smalruby/character.rb, line 202
def angle=(val)
  val %= 360
  radian = val * Math::PI / 180
  @vector[:x] = Math.cos(radian)
  @vector[:y] = Math.sin(radian)

  if @rotation_style == :free
    self.scale_x = scale_x.abs
    super(val)
  elsif @rotation_style == :left_right
    if @vector[:x] >= 0
      self.scale_x = scale_x.abs
    else
      self.scale_x = scale_x.abs * -1
    end
    super(0)
  else
    self.scale_x = scale_x.abs
    super(0)
  end
end
await() click to toggle source

1フレーム待つ

# File lib/smalruby/character.rb, line 585
def await
  Smalruby.await
end
button(pin) click to toggle source

ボタン

# File lib/smalruby/character.rb, line 465
def button(pin)
  Hardware.create_hardware(Hardware::Button, pin)
end
change_pen_color_by(val) click to toggle source

change pen color

@param [Integer] val color

# File lib/smalruby/character.rb, line 411
def change_pen_color_by(val)
  h, s, l = Color.rgb_to_hsl(*pen_color)
  @pen_color = Color.hsl_to_rgb(h + val, s, l)
end
clear() click to toggle source

Clears all pen marks from the Stage

# File lib/smalruby/character.rb, line 379
def clear
  world.current_stage.clear
end
click(buttons) click to toggle source
# File lib/smalruby/character.rb, line 532
def click(buttons)
  @event_handlers[:click].try(:each) do |h|
    if h.options.length > 0 && !h.options.any? { |b| buttons.include?(b) }
      next
    end
    @threads << h.call(Input.mouse_pos_x, Input.mouse_pos_y)
  end
end
costume_index=(val) click to toggle source
# File lib/smalruby/character.rb, line 324
def costume_index=(val)
  @costume_index = val % @costumes.length
  self.image = @costumes[@costume_index]
end
distance(x, y) click to toggle source

距離

# File lib/smalruby/character.rb, line 334
def distance(x, y)
  Math.sqrt((self.x + center_x - x).abs**2 +
            (self.y + center_y - y).abs**2).to_i
end
down_pen() click to toggle source

ペンを下ろす

# File lib/smalruby/character.rb, line 384
def down_pen
  @enable_pen = true
end
draw() click to toggle source

@!endgroup

Calls superclass method
# File lib/smalruby/character.rb, line 486
def draw
  draw_balloon if visible

  super
end
go_to(target) click to toggle source

( )に行く

# File lib/smalruby/character.rb, line 239
def go_to(target)
  if target == :mouse
    x = Input.mouse_pos_x - center_x
    y = Input.mouse_pos_y - center_y
  else
    x = target.x
    y = target.y
  end
  self.position = [x, y]
end
hit() click to toggle source
# File lib/smalruby/character.rb, line 541
def hit
  # TODO: なんでもいいからキャラクターに当たった場合に対応する
  @checking_hit_targets &= World.instance.objects
  objects = check(@checking_hit_targets)
  return if objects.empty?
  @event_handlers[:hit].try(:each) do |h|
    if h.options.length > 0 && !h.options.any? { |o| objects.include?(o) }
      next
    end
    @threads << h.call(h.options & objects)
  end
end
hit?(other) click to toggle source
# File lib/smalruby/character.rb, line 354
def hit?(other)
  check([other]).length > 0
end
join() click to toggle source
# File lib/smalruby/character.rb, line 572
def join
  @threads.compact!
  @threads.each(&:join)
end
key_down(keys) click to toggle source
# File lib/smalruby/character.rb, line 514
def key_down(keys)
  @event_handlers[:key_down].try(:each) do |h|
    if h.options.length > 0 && !h.options.any? { |k| keys.include?(k) }
      next
    end
    @threads << h.call
  end
end
key_push(keys) click to toggle source
# File lib/smalruby/character.rb, line 523
def key_push(keys)
  @event_handlers[:key_push].try(:each) do |h|
    if h.options.length > 0 && !h.options.any? { |k| keys.include?(k) }
      next
    end
    @threads << h.call
  end
end
led(pin) click to toggle source

LED

# File lib/smalruby/character.rb, line 430
def led(pin)
  Hardware.create_hardware(Hardware::Led, pin)
end
loop() { || ... } click to toggle source
# File lib/smalruby/character.rb, line 577
def loop
  Kernel.loop do
    yield
    Smalruby.await
  end
end
motor_driver(pin) click to toggle source

モータードライバ

# File lib/smalruby/character.rb, line 460
def motor_driver(pin)
  Hardware.create_hardware(Hardware::MotorDriver, pin)
end
move(val = 1) click to toggle source

( )歩動かす

# File lib/smalruby/character.rb, line 92
def move(val = 1)
  self.position = [x + @vector[:x] * val, y + @vector[:y] * val]
end
move_back(val = 1) click to toggle source

( )歩後ろに動かす

# File lib/smalruby/character.rb, line 97
def move_back(val = 1)
  move(-val)
end
neo_pixel(pin) click to toggle source

マイコン内蔵RGB LED

# File lib/smalruby/character.rb, line 445
def neo_pixel(pin)
  Hardware.create_hardware(Hardware::NeoPixel, pin)
end
next_costume() click to toggle source

次のコスチュームにする

# File lib/smalruby/character.rb, line 310
def next_costume
  self.costume_index = @costume_index + 1
end
on(event, *options, &block) click to toggle source
# File lib/smalruby/character.rb, line 492
def on(event, *options, &block)
  event = event.to_sym
  @event_handlers[event] ||= []
  h = EventHandler.new(self, options, &block)
  @event_handlers[event] << h

  case event
  when :start
    @threads << h.call if Smalruby.started?
  when :hit
    @checking_hit_targets << options
    @checking_hit_targets.flatten!
    @checking_hit_targets.uniq!
  end
end
pen_color=(val) click to toggle source

set pen color

@param [Array<Integer>|Symbol|Integer] val color

When color is Array<Integer>, it means [R, G, B].
When color is Symbol, it means the color code; like :white, :black, etc...
When color is Integer, it means hue.
# File lib/smalruby/character.rb, line 399
def pen_color=(val)
  if val.is_a?(Numeric)
    val %= 201
    _, s, l = Color.rgb_to_hsl(*pen_color)
    val = Color.hsl_to_rgb(val, s, l)
  end
  @pen_color = val
end
pen_shade=(val) click to toggle source

set pen shade

@param Integer val shade

# File lib/smalruby/character.rb, line 419
def pen_shade=(val)
  val %= 101
  h, s, = *Color.rgb_to_hsl(*pen_color)
  @pen_color = Color.hsl_to_rgb(h, s, val)
end
play(option = {}) click to toggle source

( )の音を鳴らす

# File lib/smalruby/character.rb, line 363
def play(option = {})
  defaults = {
    name: 'piano_do.wav'
  }
  opt = process_optional_arguments(option, defaults)

  sound = new_sound(opt[:name])
  sound.set_volume(calc_volume)
  sound.play
end
point_towards(target) click to toggle source

( )に向ける

# File lib/smalruby/character.rb, line 225
def point_towards(target)
  if target == :mouse
    tx = Input.mouse_pos_x
    ty = Input.mouse_pos_y
  else
    tx = target.x
    ty = target.y
  end
  dx = tx - x
  dy = ty - y
  self.angle = Math.atan2(dy, dx) * 180 / Math::PI
end
position() click to toggle source

X座標、Y座標

# File lib/smalruby/character.rb, line 149
def position
  [x, y]
end
position=(val) click to toggle source

X座標を( )、Y座標を( )にする

# File lib/smalruby/character.rb, line 133
def position=(val)
  if @enable_pen
    @enable_pen = false
    left = x + center_x
    top = y + center_y
    self.x = val[0]
    self.y = val[1]
    draw_pen(left, top, x + center_x, y + center_y)
    @enable_pen = true
  else
    self.x = val[0]
    self.y = val[1]
  end
end
reach_left_or_right_wall?() click to toggle source

左右の端に着いた?

# File lib/smalruby/character.rb, line 345
def reach_left_or_right_wall?
  x <= 0 || x >= (Window.width - image.width)
end
reach_top_or_bottom_wall?() click to toggle source

上下の端に着いた?

# File lib/smalruby/character.rb, line 350
def reach_top_or_bottom_wall?
  y <= 0 || y >= (Window.height - image.height)
end
reach_wall?() click to toggle source

端に着いた?

# File lib/smalruby/character.rb, line 340
def reach_wall?
  reach_left_or_right_wall? || reach_top_or_bottom_wall?
end
rgb_led_anode(pin) click to toggle source

RGB LED(アノード)

# File lib/smalruby/character.rb, line 435
def rgb_led_anode(pin)
  Hardware.create_hardware(Hardware::RgbLedAnode, pin)
end
rgb_led_cathode(pin) click to toggle source

RGB LED(カソード)

# File lib/smalruby/character.rb, line 440
def rgb_led_cathode(pin)
  Hardware.create_hardware(Hardware::RgbLedCathode, pin)
end
rotate(angle) click to toggle source

( )度回転する

# File lib/smalruby/character.rb, line 182
def rotate(angle)
  self.angle += angle
end
rotation_style=(val) click to toggle source
# File lib/smalruby/character.rb, line 186
def rotation_style=(val)
  @rotation_style = val
  sync_angle(@vector[:x], @vector[:y])
end
say(options = {}) click to toggle source

@!group 見た目

# File lib/smalruby/character.rb, line 254
def say(options = {})
  defaults = {
    message: '',
    second: 0,
  }
  opts = process_optional_arguments(options, defaults)

  message = opts[:message].to_s
  return if message == @current_message

  @current_message = message

  if @balloon
    @balloon.vanish
    @balloon = nil
  end

  return if message.empty?

  lines = message.to_s.lines.map { |l| l.scan(/.{1,10}/) }.flatten
  font = new_font(16)
  width = lines.map { |l| font.get_width(l) }.max
  height = lines.length * (font.size + 1)
  frame_size = 3
  margin_size = 3
  image = Image.new(width + (frame_size + margin_size) * 2,
                    height + (frame_size + margin_size) * 2)
  image.box_fill(0,
                 0,
                 width + (frame_size + margin_size) * 2 - 1,
                 height + (frame_size + margin_size) * 2 - 1,
                 [125, 125, 125])
  image.box_fill(frame_size,
                 frame_size,
                 width + (frame_size + margin_size) + margin_size - 1,
                 height + (frame_size + margin_size) + margin_size - 1,
                 [255, 255, 255])
  lines.each.with_index do |line, row|
    image.draw_font(frame_size + margin_size,
                    frame_size + margin_size + (font.size + 1) * row,
                    line, font, [0, 0, 0])
  end
  @balloon = Sprite.new(x, y, image)
end
sensor(pin) click to toggle source

汎用的なセンサー

# File lib/smalruby/character.rb, line 470
def sensor(pin)
  Hardware.create_hardware(Hardware::Sensor, pin)
end
servo(pin) click to toggle source

サーボモーター

# File lib/smalruby/character.rb, line 450
def servo(pin)
  Hardware.create_hardware(Hardware::Servo, pin)
end
smalrubot_s1() click to toggle source

create or get Hardware::SmalrubotS1 instance

# File lib/smalruby/character.rb, line 480
def smalrubot_s1
  Hardware.create_hardware(Hardware::SmalrubotS1)
end
smalrubot_v3() click to toggle source

create or get Hardware::SmalrubotV3 instance

# File lib/smalruby/character.rb, line 475
def smalrubot_v3
  Hardware.create_hardware(Hardware::SmalrubotV3)
end
start() click to toggle source
# File lib/smalruby/character.rb, line 508
def start
  @event_handlers[:start].try(:each) do |h|
    @threads << h.call
  end
end
switch_costume(name) click to toggle source

コスチュームを( )にする

# File lib/smalruby/character.rb, line 315
def switch_costume(name)
  if @costume_name__index.key?(name)
    index = @costume_name__index[name]
  else
    index = 0
  end
  self.costume_index = index
end
turn() click to toggle source

くるっと振り返る

# File lib/smalruby/character.rb, line 154
def turn
  sync_angle(@vector[:x] * -1, @vector[:y] * -1)
end
turn_if_reach_wall() click to toggle source

もし端に着いたら、跳ね返る

# File lib/smalruby/character.rb, line 169
def turn_if_reach_wall
  lr = reach_left_or_right_wall?
  tb = reach_top_or_bottom_wall?
  if lr && tb
    turn
  elsif lr
    turn_x
  elsif tb
    turn_y
  end
end
turn_x() click to toggle source

横に振り返る

# File lib/smalruby/character.rb, line 159
def turn_x
  sync_angle(@vector[:x] * -1, @vector[:y])
end
turn_y() click to toggle source

縦に振り返る

# File lib/smalruby/character.rb, line 164
def turn_y
  sync_angle(@vector[:x], @vector[:y] * -1)
end
two_wheel_drive_car(pin) click to toggle source

2WD車

# File lib/smalruby/character.rb, line 455
def two_wheel_drive_car(pin)
  Hardware.create_hardware(Hardware::TwoWheelDriveCar, pin)
end
up_pen() click to toggle source

ペンを上げる

# File lib/smalruby/character.rb, line 389
def up_pen
  @enable_pen = false
end
visible=(val) click to toggle source

表示する/隠す

Calls superclass method
# File lib/smalruby/character.rb, line 300
def visible=(val)
  if val
    self.collision_enable = true
  else
    self.collision_enable = false
  end
  super
end
x=(val) click to toggle source

X座標を( )にする

Calls superclass method
# File lib/smalruby/character.rb, line 102
def x=(val)
  left = x + center_x
  top = y + center_y

  if val < 0
    val = 0
  elsif val + image.width >= Window.width
    val = Window.width - image.width
  end

  super(val)

  draw_pen(left, top, x + center_x, y + center_y) if @enable_pen
end
y=(val) click to toggle source

Y座標を( )にする

Calls superclass method
# File lib/smalruby/character.rb, line 118
def y=(val)
  left = x + center_x
  top = y + center_y

  if val < 0
    val = 0
  elsif val + image.height >= Window.height
    val = Window.height - image.height
  end
  super(val)

  draw_pen(left, top, x + center_x, y + center_y) if @enable_pen
end

Private Instance Methods

asset_path(name) click to toggle source
# File lib/smalruby/character.rb, line 604
def asset_path(name)
  program_path = Pathname($PROGRAM_NAME).expand_path(Dir.pwd)
  paths = [Pathname("../#{name}").expand_path(program_path),
           Pathname("../__assets__/#{name}").expand_path(program_path),
           Pathname("../../../assets/#{name}").expand_path(__FILE__)]
  paths.find { |path| path.file? }.to_s
end
calc_volume() click to toggle source
# File lib/smalruby/character.rb, line 660
def calc_volume
  (255 * @volume / 100.0).to_i
end
draw_balloon() click to toggle source
# File lib/smalruby/character.rb, line 626
def draw_balloon
  if @balloon
    @balloon.x = x + image.width / 2
    if @balloon.x < 0
      @balloon.x = 0
    elsif @balloon.x + @balloon.image.width >= Window.width
      @balloon.x = Window.width - @balloon.image.width
    end
    @balloon.y = y - @balloon.image.height
    if @balloon.y < 0
      @balloon.y = 0
    elsif @balloon.y + @balloon.image.height >= Window.height
      @balloon.y = Window.height - @balloon.image.height
    end
    @balloon.draw
  end
end
draw_pen(left, top, right, bottom) click to toggle source
# File lib/smalruby/character.rb, line 591
def draw_pen(left, top, right, bottom)
  return if Util.raspberrypi? || !visible || vanished?
  world.current_stage.line(left: left, top: top,
                           right: right, bottom: bottom,
                           color: @pen_color)
end
new_font(size) click to toggle source
# File lib/smalruby/character.rb, line 612
def new_font(size)
  self.class.font_cache.synchronize do
    self.class.font_cache[size] ||= Font.new(size)
  end
  self.class.font_cache[size]
end
new_sound(name) click to toggle source
# File lib/smalruby/character.rb, line 619
def new_sound(name)
  self.class.sound_cache.synchronize do
    self.class.sound_cache[name] ||= Sound.new(asset_path(name))
  end
  self.class.sound_cache[name]
end
print_exception(exception) click to toggle source
process_optional_arguments(options, defaults) click to toggle source
# File lib/smalruby/character.rb, line 644
def process_optional_arguments(options, defaults)
  unknown_keys = options.keys - defaults.keys
  if unknown_keys.length > 0
    s = unknown_keys.map { |k| "#{k}: #{options[k].inspect}" }.join(', ')
    fail ArgumentError, "Unknown options: #{s}"
  end
  defaults.merge(options)
end
sync_angle(x, y) click to toggle source
# File lib/smalruby/character.rb, line 598
def sync_angle(x, y)
  a = Math.acos(x / Math.sqrt(x**2 + y**2)) * 180 / Math::PI
  a = 360 - a if y < 0
  self.angle = a
end