Class: Smalruby::Character

Inherits:
Sprite
  • Object
show all
Extended by:
Forwardable, Mutex_m
Defined in:
lib/smalruby/character.rb

Overview

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

Direct Known Subclasses

Canvas, Console

Instance Attribute Summary collapse

動き collapse

見た目 collapse

調べる collapse

collapse

ペン collapse

ハードウェア collapse

Instance Method Summary collapse

Constructor Details

#initialize(option = {}) ⇒ Character

Returns a new instance of Character.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 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'

  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

Instance Attribute Details

#checking_hit_targetsObject

Returns the value of attribute checking_hit_targets.



24
25
26
# File 'lib/smalruby/character.rb', line 24

def checking_hit_targets
  @checking_hit_targets
end

#costume_indexObject

Returns the value of attribute costume_index.



26
27
28
# File 'lib/smalruby/character.rb', line 26

def costume_index
  @costume_index
end

#costumesObject

Returns the value of attribute costumes.



25
26
27
# File 'lib/smalruby/character.rb', line 25

def costumes
  @costumes
end

#enable_penObject (readonly)

Returns the value of attribute enable_pen.



28
29
30
# File 'lib/smalruby/character.rb', line 28

def enable_pen
  @enable_pen
end

#event_handlersObject

Returns the value of attribute event_handlers.



22
23
24
# File 'lib/smalruby/character.rb', line 22

def event_handlers
  @event_handlers
end

#pen_colorObject

Returns the value of attribute pen_color.



29
30
31
# File 'lib/smalruby/character.rb', line 29

def pen_color
  @pen_color
end

#rotation_styleObject

Returns the value of attribute rotation_style.



27
28
29
# File 'lib/smalruby/character.rb', line 27

def rotation_style
  @rotation_style
end

#threadsObject

Returns the value of attribute threads.



23
24
25
# File 'lib/smalruby/character.rb', line 23

def threads
  @threads
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'lib/smalruby/character.rb', line 517

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

#angleObject

角度



191
192
193
194
195
196
197
198
# File 'lib/smalruby/character.rb', line 191

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) ⇒ Object

( )度に向ける



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/smalruby/character.rb', line 201

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 = 1
    super(val)
  elsif @rotation_style == :left_right
    if @vector[:x] >= 0
      self.scale_x = 1
    else
      self.scale_x = -1
    end
    super(0)
  else
    self.scale_x = 1
    super(0)
  end
end

#awaitObject

1フレーム待つ



548
549
550
# File 'lib/smalruby/character.rb', line 548

def await
  Smalruby.await
end

#button(pin) ⇒ Object

ボタン



428
429
430
# File 'lib/smalruby/character.rb', line 428

def button(pin)
  Hardware.create_hardware(Hardware::Button, pin)
end

#click(buttons) ⇒ Object



495
496
497
498
499
500
501
502
# File 'lib/smalruby/character.rb', line 495

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

#distance(x, y) ⇒ Object

距離



333
334
335
336
# File 'lib/smalruby/character.rb', line 333

def distance(x, y)
  Math.sqrt((self.x + center_x - x).abs**2 +
            (self.y + center_y - y).abs**2).to_i
end

#down_penObject

ペンを下ろす



376
377
378
# File 'lib/smalruby/character.rb', line 376

def down_pen
  @enable_pen = true
end

#drawObject



449
450
451
452
453
# File 'lib/smalruby/character.rb', line 449

def draw
  draw_balloon if visible

  super
end

#go_to(target) ⇒ Object

( )に行く



238
239
240
241
242
243
244
245
246
247
# File 'lib/smalruby/character.rb', line 238

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

#hitObject



504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/smalruby/character.rb', line 504

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) ⇒ Boolean

Returns:

  • (Boolean)


353
354
355
# File 'lib/smalruby/character.rb', line 353

def hit?(other)
  check([other]).length > 0
end

#joinObject



535
536
537
538
# File 'lib/smalruby/character.rb', line 535

def join
  @threads.compact!
  @threads.each(&:join)
end

#key_down(keys) ⇒ Object



477
478
479
480
481
482
483
484
# File 'lib/smalruby/character.rb', line 477

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) ⇒ Object



486
487
488
489
490
491
492
493
# File 'lib/smalruby/character.rb', line 486

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) ⇒ Object

LED



393
394
395
# File 'lib/smalruby/character.rb', line 393

def led(pin)
  Hardware.create_hardware(Hardware::Led, pin)
end

#loopObject



540
541
542
543
544
545
# File 'lib/smalruby/character.rb', line 540

def loop
  Kernel.loop do
    yield
    Smalruby.await
  end
end

#motor_driver(pin) ⇒ Object

モータードライバ



423
424
425
# File 'lib/smalruby/character.rb', line 423

def motor_driver(pin)
  Hardware.create_hardware(Hardware::MotorDriver, pin)
end

#move(val = 1) ⇒ Object

( )歩動かす



91
92
93
# File 'lib/smalruby/character.rb', line 91

def move(val = 1)
  self.position = [x + @vector[:x] * val, y + @vector[:y] * val]
end

#move_back(val = 1) ⇒ Object

( )歩後ろに動かす



96
97
98
# File 'lib/smalruby/character.rb', line 96

def move_back(val = 1)
  move(-val)
end

#neo_pixel(pin) ⇒ Object

マイコン内蔵RGB LED



408
409
410
# File 'lib/smalruby/character.rb', line 408

def neo_pixel(pin)
  Hardware.create_hardware(Hardware::NeoPixel, pin)
end

#next_costumeObject

次のコスチュームにする



309
310
311
# File 'lib/smalruby/character.rb', line 309

def next_costume
  self.costume_index = @costume_index + 1
end

#on(event, *options, &block) ⇒ Object



455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/smalruby/character.rb', line 455

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

#play(option = {}) ⇒ Object

( )の音を鳴らす



362
363
364
365
366
367
368
369
# File 'lib/smalruby/character.rb', line 362

def play(option = {})
  defaults = {
    name: 'piano_do.wav'
  }
  opt = process_optional_arguments(option, defaults)

  new_sound(opt[:name]).play
end

#point_towards(target) ⇒ Object

( )に向ける



224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/smalruby/character.rb', line 224

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

#positionObject

X座標、Y座標



148
149
150
# File 'lib/smalruby/character.rb', line 148

def position
  [x, y]
end

#position=(val) ⇒ Object

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



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/smalruby/character.rb', line 132

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?Boolean

左右の端に着いた?

Returns:

  • (Boolean)


344
345
346
# File 'lib/smalruby/character.rb', line 344

def reach_left_or_right_wall?
  x <= 0 || x >= (Window.width - image.width)
end

#reach_top_or_bottom_wall?Boolean

上下の端に着いた?

Returns:

  • (Boolean)


349
350
351
# File 'lib/smalruby/character.rb', line 349

def reach_top_or_bottom_wall?
  y <= 0 || y >= (Window.height - image.height)
end

#reach_wall?Boolean

端に着いた?

Returns:

  • (Boolean)


339
340
341
# File 'lib/smalruby/character.rb', line 339

def reach_wall?
  reach_left_or_right_wall? || reach_top_or_bottom_wall?
end

#rgb_led_anode(pin) ⇒ Object

RGB LED(アノード)



398
399
400
# File 'lib/smalruby/character.rb', line 398

def rgb_led_anode(pin)
  Hardware.create_hardware(Hardware::RgbLedAnode, pin)
end

#rgb_led_cathode(pin) ⇒ Object

RGB LED(カソード)



403
404
405
# File 'lib/smalruby/character.rb', line 403

def rgb_led_cathode(pin)
  Hardware.create_hardware(Hardware::RgbLedCathode, pin)
end

#rotate(angle) ⇒ Object

( )度回転する



181
182
183
# File 'lib/smalruby/character.rb', line 181

def rotate(angle)
  self.angle += angle
end

#say(options = {}) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/smalruby/character.rb', line 253

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) ⇒ Object

汎用的なセンサー



433
434
435
# File 'lib/smalruby/character.rb', line 433

def sensor(pin)
  Hardware.create_hardware(Hardware::Sensor, pin)
end

#servo(pin) ⇒ Object

サーボモーター



413
414
415
# File 'lib/smalruby/character.rb', line 413

def servo(pin)
  Hardware.create_hardware(Hardware::Servo, pin)
end

#smalrubot_s1Object

create or get Hardware::SmalrubotS1 instance



443
444
445
# File 'lib/smalruby/character.rb', line 443

def smalrubot_s1
  Hardware.create_hardware(Hardware::SmalrubotS1)
end

#smalrubot_v3Object

create or get Hardware::SmalrubotV3 instance



438
439
440
# File 'lib/smalruby/character.rb', line 438

def smalrubot_v3
  Hardware.create_hardware(Hardware::SmalrubotV3)
end

#startObject



471
472
473
474
475
# File 'lib/smalruby/character.rb', line 471

def start
  @event_handlers[:start].try(:each) do |h|
    @threads << h.call
  end
end

#switch_costume(name) ⇒ Object

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



314
315
316
317
318
319
320
321
# File 'lib/smalruby/character.rb', line 314

def switch_costume(name)
  if @costume_name__index.key?(name)
    index = @costume_name__index[name]
  else
    index = 0
  end
  self.costume_index = index
end

#turnObject

くるっと振り返る



153
154
155
# File 'lib/smalruby/character.rb', line 153

def turn
  sync_angle(@vector[:x] * -1, @vector[:y] * -1)
end

#turn_if_reach_wallObject

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



168
169
170
171
172
173
174
175
176
177
178
# File 'lib/smalruby/character.rb', line 168

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_xObject

横に振り返る



158
159
160
# File 'lib/smalruby/character.rb', line 158

def turn_x
  sync_angle(@vector[:x] * -1, @vector[:y])
end

#turn_yObject

縦に振り返る



163
164
165
# File 'lib/smalruby/character.rb', line 163

def turn_y
  sync_angle(@vector[:x], @vector[:y] * -1)
end

#two_wheel_drive_car(pin) ⇒ Object

2WD車



418
419
420
# File 'lib/smalruby/character.rb', line 418

def two_wheel_drive_car(pin)
  Hardware.create_hardware(Hardware::TwoWheelDriveCar, pin)
end

#up_penObject

ペンを上げる



381
382
383
# File 'lib/smalruby/character.rb', line 381

def up_pen
  @enable_pen = false
end

#visible=(val) ⇒ Object

表示する/隠す



299
300
301
302
303
304
305
306
# File 'lib/smalruby/character.rb', line 299

def visible=(val)
  if val
    self.collision_enable = true
  else
    self.collision_enable = false
  end
  super
end

#x=(val) ⇒ Object

X座標を( )にする



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/smalruby/character.rb', line 101

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) ⇒ Object

Y座標を( )にする



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/smalruby/character.rb', line 117

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