Last active
March 22, 2025 00:12
-
-
Save takeru/5fe4f5512dfde678403a9a688cbe7eee to your computer and use it in GitHub Desktop.
M5 Atom Matrix LED Demo for OpenBlink
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix Beautiful LED Demo | |
| # 自然現象と数学的パターンを組み合わせた魅力的なデモ | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # | |
| # 使用できない機能: | |
| # - Math モジュール(sin, cos, sqrt, atan2など) | |
| # - rand - 乱数生成 | |
| # - LED.get - 現在のLED色の取得 | |
| # - respond_to? - メソッド存在チェック | |
| # - reverse, reverse_each - 配列の逆順処理 | |
| # - 浮動小数点数の%演算 | |
| # - backtrace - エラーのスタックトレース | |
| # | |
| # 代替手段: | |
| # - Math.sin → sin_approx関数で近似 | |
| # - Math.sqrt → 単純な距離計算(マンハッタン距離など) | |
| # - Math.atan2 → 簡易的な角度計算 | |
| # - rand → 線形合同法で疑似乱数を自前で実装 | |
| # - reverse_each → downtoで代用 | |
| # - LED.get → 固定値で代用 | |
| # - 浮動小数点の剰余 → 整数での計算に変更 | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 # 0-255の範囲に正規化 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # オーロラのような波動パターン | |
| def aurora_wave | |
| phase = 0 | |
| intensity = 0 | |
| direction = 1 | |
| 50.times do | |
| 5.times do |y| | |
| 5.times do |x| | |
| wave = (sin_approx((x + phase) * 51) * 128) / 64 + 128 | |
| hue = (wave + y * 20 + phase * 5) % 256 | |
| brightness = intensity + ((sin_approx((x + y + phase) * 51) * 32) / 64 + 32) | |
| rgb = hsv_to_rgb(hue, 255, brightness) | |
| LED.set(rgb, y * 5 + x) | |
| end | |
| end | |
| phase = (phase + 1) % 256 | |
| intensity += direction | |
| if intensity >= 192 || intensity <= 64 | |
| direction = -direction | |
| end | |
| sleep 0.05 | |
| end | |
| end | |
| # 銀河渦巻きパターン | |
| def galaxy_spiral | |
| center_x = 2 | |
| center_y = 2 | |
| angle = 0 | |
| 60.times do | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| dx = x - center_x | |
| dy = y - center_y | |
| # マンハッタン距離を使用 | |
| distance = dx.abs + dy.abs | |
| # 簡易角度計算 | |
| spiral_angle = (((dx * 128) / (distance + 1)) + angle) % 256 | |
| hue = (spiral_angle + distance * 32) % 256 | |
| brightness = 255 - (distance * 48) | |
| brightness = brightness < 0 ? 0 : brightness | |
| rgb = hsv_to_rgb(hue, 192, brightness) | |
| LED.set(rgb, i) | |
| end | |
| angle = (angle + 3) % 256 | |
| sleep 0.05 | |
| end | |
| end | |
| # 量子の波動関数的なパターン | |
| def quantum_wave | |
| time = 0 | |
| particles = [] | |
| 5.times do |i| | |
| particles << [(i * 51) % 256, i * 5] # [phase, position] | |
| end | |
| 40.times do | |
| # 古い粒子の跡をフェードアウト | |
| 25.times do |i| | |
| LED.set([1, 1, 1], i) | |
| end | |
| # 粒子の更新と描画 | |
| particles.each_with_index do |p, idx| | |
| phase, pos = p | |
| # 波動関数的な動き | |
| wave_effect = ((sin_approx(time * 25) * 2) / 64 + 2) % 5 | |
| new_pos = (pos + wave_effect) % 25 | |
| particles[idx][1] = new_pos | |
| # 干渉パターンの生成 | |
| 3.times do |i| | |
| interference_pos = (new_pos + i) % 25 | |
| hue = (phase + i * 32 + time * 2) % 256 | |
| rgb = hsv_to_rgb(hue, 255, 255 - i * 64) | |
| LED.set(rgb, interference_pos) | |
| end | |
| end | |
| time += 1 | |
| sleep 0.05 | |
| end | |
| end | |
| # 生命の樹パターン | |
| def tree_of_life | |
| energy_points = [[2, 0], [1, 1], [3, 1], [2, 2], [0, 2], [4, 2], [1, 3], [3, 3], [2, 4]] | |
| base_hue = 0 | |
| 45.times do | |
| # エネルギーの流れを表現 | |
| energy_points.each_with_index do |point, i| | |
| x, y = point | |
| pos = y * 5 + x | |
| # エネルギーの脈動 | |
| pulse = (sin_approx(i + base_hue * 25) * 128) / 64 + 128 | |
| hue = (base_hue + pulse / 2) % 256 | |
| rgb = hsv_to_rgb(hue, 192 + pulse / 4, 192 + pulse / 4) | |
| LED.set(rgb, pos) | |
| # エネルギーの放射 | |
| [[-1,0], [1,0], [0,-1], [0,1]].each do |dx, dy| | |
| nx = x + dx | |
| ny = y + dy | |
| if nx >= 0 && nx < 5 && ny >= 0 && ny < 5 | |
| npos = ny * 5 + nx | |
| rgb2 = hsv_to_rgb(hue, 128, pulse / 2) | |
| LED.set(rgb2, npos) | |
| end | |
| end | |
| end | |
| base_hue = (base_hue + 1) % 256 | |
| sleep 0.05 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| aurora_wave # オーロラのような波動 | |
| sleep 0.2 | |
| galaxy_spiral # 銀河の渦巻き | |
| sleep 0.2 | |
| quantum_wave # 量子の波動関数 | |
| sleep 0.2 | |
| tree_of_life # 生命の樹 | |
| sleep 0.2 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラーモードでLEDをゆっくりと赤く点滅 | |
| while true do | |
| 25.times { |i| LED.set([32, 0, 0], i) } | |
| sleep 1 | |
| 25.times { |i| LED.set([0, 0, 0], i) } | |
| sleep 1 | |
| break if Blink.req_reload? | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix LED Demo for OpenBlink | |
| # Demo patterns ported from C++ to Ruby | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # | |
| # 使用できない機能: | |
| # - LED.get - 現在のLED色の取得 | |
| # - rand - 乱数生成 | |
| # - respond_to? - メソッド存在チェック | |
| # - reverse, reverse_each - 配列の逆順処理 | |
| # - 浮動小数点数の%演算 | |
| # - backtrace - エラーのスタックトレース | |
| # | |
| # 代替手段: | |
| # - 乱数 → 線形合同法で疑似乱数を自前で実装 | |
| # - reverse_each → downtoで代用 | |
| # - LED.get → 固定値で代用 | |
| # - 浮動小数点の剰余 → 整数での計算に変更 | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 # 整数での剰余演算 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| # 256スケールで計算 | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| # 最終的な値を4で割って明るさを1/4に | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| def demo1 | |
| # 渦巻きパターンの座標定義 | |
| spiral = [ | |
| 12,17,22,23,24, | |
| 19,18,13,8,3, | |
| 4,9,14,13,12, | |
| 11,6,1,2,7, | |
| 8,13,18,17,16 | |
| ] | |
| hue = 0 | |
| # 渦巻き状に点灯 | |
| spiral.each_with_index do |pos, i| | |
| h = (hue + (i * 256/25)) % 256 | |
| rgb = hsv_to_rgb(h, 255, 255) | |
| LED.set(rgb, pos) | |
| sleep 0.05 | |
| end | |
| # 消灯エフェクト | |
| (spiral.size - 1).downto(0) do |i| | |
| LED.set([0, 0, 0], spiral[i]) | |
| sleep 0.05 | |
| end | |
| end | |
| def demo2 | |
| hue = 0 | |
| drops = [0, 0, 0, 0, 0] # 各列の落下位置 | |
| drop_speed = [1, 2, 1, 2, 1] # 各列の落下速度 | |
| wave = 0 # 波紋の中心位置 | |
| 25.times do | |
| # マトリックス風の落下エフェクト | |
| 5.times do |x| | |
| # 落下する粒子の位置を更新 | |
| drops[x] = (drops[x] + drop_speed[x]) % 10 | |
| # 各列に光の粒子を描画 | |
| 5.times do |y| | |
| pos = y * 5 + x | |
| if y == drops[x] / 2 | |
| rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| elsif y == (drops[x] / 2) - 1 | |
| rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 128) | |
| LED.set(rgb, pos) | |
| else | |
| # フェードアウト効果を固定値で代用 | |
| LED.set([2, 2, 2], pos) | |
| end | |
| end | |
| end | |
| # 波紋エフェクト | |
| center_x = wave % 5 | |
| center_y = wave / 5 | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| distance = (x - center_x).abs + (y - center_y).abs # マンハッタン距離 | |
| if distance < 3 | |
| rgb = hsv_to_rgb((hue + distance * 32) % 256, 192, 255 / (distance + 1)) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| sleep 0.05 | |
| hue = (hue + 3) % 256 | |
| wave = (wave + 1) % 25 | |
| end | |
| end | |
| def demo3 | |
| hue = 0 | |
| pulse = 0 | |
| pattern = 0 | |
| pseudo_random = 0 # 疑似ランダム値 | |
| 100.times do | |
| # 全LEDをフェードアウト(固定値で代用) | |
| 25.times do |i| | |
| LED.set([2, 2, 2], i) | |
| end | |
| case pattern % 4 | |
| when 0 # 爆発エフェクト | |
| center_led = 12 | |
| 25.times do |i| | |
| distance = ((i % 5) - (center_led % 5)).abs + ((i / 5) - (center_led / 5)).abs | |
| if distance == pulse / 8 | |
| rgb = hsv_to_rgb((hue + distance * 30) % 256, 255, 255) | |
| LED.set(rgb, i) | |
| end | |
| end | |
| when 1 # ジグザグパターン | |
| 5.times do |i| | |
| pos = ((pulse + i) % 5) * 5 + (((pulse + i) / 5) % 2 == 1 ? 4 - i : i) | |
| rgb = hsv_to_rgb((hue + i * 50) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| end | |
| when 2 # ランダムフラッシュ(疑似ランダムに変更) | |
| 3.times do | |
| pseudo_random = (pseudo_random * 17 + 23) % 25 # 簡単な線形合同法 | |
| rgb = hsv_to_rgb((hue + pseudo_random * 8) % 256, 255, 255) | |
| LED.set(rgb, pseudo_random) | |
| end | |
| if pulse % 8 == 0 | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(hue % 256, 128, 255) | |
| LED.set(rgb, i) | |
| end | |
| end | |
| when 3 # 螺旋状の高速回転 | |
| rotation_pos = pulse % 25 | |
| 5.times do |i| | |
| pos = (rotation_pos + i * 5) % 25 | |
| rgb = hsv_to_rgb((hue + i * 32) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| # 高速なストロボ効果(疑似ランダムに変更) | |
| pseudo_random = (pseudo_random * 17 + 23) % 100 # 簡単な線形合同法 | |
| if pseudo_random < 20 # 20%の確率で発生 | |
| 25.times do |i| | |
| LED.set([64, 64, 64], i) # ストロボの明るさも調整 | |
| end | |
| sleep 0.005 | |
| 25.times do |i| | |
| LED.set([0, 0, 0], i) | |
| end | |
| end | |
| sleep 0.02 | |
| pulse += 1 | |
| hue = (hue + 3) % 256 | |
| # パターン切り替え | |
| if pulse % 64 == 0 | |
| pattern += 1 | |
| # 切り替え時の特殊エフェクト | |
| 25.times do |i| | |
| rgb = hsv_to_rgb((hue + i * 7) % 256, 255, 255) | |
| LED.set(rgb, i) | |
| end | |
| sleep 0.05 | |
| end | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| demo1 | |
| sleep 0.5 | |
| demo2 | |
| sleep 0.5 | |
| demo3 | |
| sleep 0.5 | |
| # OpenBlinkのリロードチェック | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "Error occurred:" | |
| puts " Class: #{e.class}" | |
| puts " Message: #{e.message}" | |
| # エラーモードでLEDを赤く点滅 | |
| n = 0 | |
| while true do | |
| puts "Error mode: cycle #{n}" | |
| 25.times { |i| LED.set([64, 0, 0], i) } # 赤色で点灯 | |
| sleep 0.5 | |
| 25.times { |i| LED.set([0, 0, 0], i) } # 消灯 | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| n += 1 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix LED Demo for OpenBlink | |
| # Demo patterns ported from C++ to Ruby | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 # 整数での剰余演算 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| # 256スケールで計算 | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| # 最終的な値を4で割って明るさを1/4に | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| def demo1 | |
| # 渦巻きパターンの座標定義 | |
| spiral = [ | |
| 12,17,22,23,24, | |
| 19,18,13,8,3, | |
| 4,9,14,13,12, | |
| 11,6,1,2,7, | |
| 8,13,18,17,16 | |
| ] | |
| hue = 0 | |
| # 渦巻き状に点灯 | |
| spiral.each_with_index do |pos, i| | |
| h = (hue + (i * 256/25)) % 256 | |
| rgb = hsv_to_rgb(h, 255, 255) | |
| LED.set(rgb, pos) | |
| sleep 0.05 | |
| end | |
| # 消灯エフェクト | |
| (spiral.size - 1).downto(0) do |i| | |
| LED.set([0, 0, 0], spiral[i]) | |
| sleep 0.05 | |
| end | |
| end | |
| def demo2 | |
| hue = 0 | |
| drops = [0, 0, 0, 0, 0] # 各列の落下位置 | |
| drop_speed = [1, 2, 1, 2, 1] # 各列の落下速度 | |
| wave = 0 # 波紋の中心位置 | |
| 25.times do | |
| # マトリックス風の落下エフェクト | |
| 5.times do |x| | |
| # 落下する粒子の位置を更新 | |
| drops[x] = (drops[x] + drop_speed[x]) % 10 | |
| # 各列に光の粒子を描画 | |
| 5.times do |y| | |
| pos = y * 5 + x | |
| if y == drops[x] / 2 | |
| rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| elsif y == (drops[x] / 2) - 1 | |
| rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 128) | |
| LED.set(rgb, pos) | |
| else | |
| # フェードアウト効果を固定値で代用 | |
| LED.set([2, 2, 2], pos) | |
| end | |
| end | |
| end | |
| # 波紋エフェクト | |
| center_x = wave % 5 | |
| center_y = wave / 5 | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| distance = (x - center_x).abs + (y - center_y).abs # マンハッタン距離 | |
| if distance < 3 | |
| rgb = hsv_to_rgb((hue + distance * 32) % 256, 192, 255 / (distance + 1)) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| sleep 0.05 | |
| hue = (hue + 3) % 256 | |
| wave = (wave + 1) % 25 | |
| end | |
| end | |
| def demo3 | |
| hue = 0 | |
| pulse = 0 | |
| pattern = 0 | |
| pseudo_random = 0 # 疑似ランダム値 | |
| 100.times do | |
| # 全LEDをフェードアウト(固定値で代用) | |
| 25.times do |i| | |
| LED.set([2, 2, 2], i) | |
| end | |
| case pattern % 4 | |
| when 0 # 爆発エフェクト | |
| center_led = 12 | |
| 25.times do |i| | |
| distance = ((i % 5) - (center_led % 5)).abs + ((i / 5) - (center_led / 5)).abs | |
| if distance == pulse / 8 | |
| rgb = hsv_to_rgb((hue + distance * 30) % 256, 255, 255) | |
| LED.set(rgb, i) | |
| end | |
| end | |
| when 1 # ジグザグパターン | |
| 5.times do |i| | |
| pos = ((pulse + i) % 5) * 5 + (((pulse + i) / 5) % 2 == 1 ? 4 - i : i) | |
| rgb = hsv_to_rgb((hue + i * 50) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| end | |
| when 2 # ランダムフラッシュ(疑似ランダムに変更) | |
| 3.times do | |
| pseudo_random = (pseudo_random * 17 + 23) % 25 # 簡単な線形合同法 | |
| rgb = hsv_to_rgb((hue + pseudo_random * 8) % 256, 255, 255) | |
| LED.set(rgb, pseudo_random) | |
| end | |
| if pulse % 8 == 0 | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(hue % 256, 128, 255) | |
| LED.set(rgb, i) | |
| end | |
| end | |
| when 3 # 螺旋状の高速回転 | |
| rotation_pos = pulse % 25 | |
| 5.times do |i| | |
| pos = (rotation_pos + i * 5) % 25 | |
| rgb = hsv_to_rgb((hue + i * 32) % 256, 255, 255) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| # 高速なストロボ効果(疑似ランダムに変更) | |
| pseudo_random = (pseudo_random * 17 + 23) % 100 # 簡単な線形合同法 | |
| if pseudo_random < 20 # 20%の確率で発生 | |
| 25.times do |i| | |
| LED.set([64, 64, 64], i) # ストロボの明るさも調整 | |
| end | |
| sleep 0.005 | |
| 25.times do |i| | |
| LED.set([0, 0, 0], i) | |
| end | |
| end | |
| sleep 0.02 | |
| pulse += 1 | |
| hue = (hue + 3) % 256 | |
| # パターン切り替え | |
| if pulse % 64 == 0 | |
| pattern += 1 | |
| # 切り替え時の特殊エフェクト | |
| 25.times do |i| | |
| rgb = hsv_to_rgb((hue + i * 7) % 256, 255, 255) | |
| LED.set(rgb, i) | |
| end | |
| sleep 0.05 | |
| end | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| demo1 | |
| sleep 0.5 | |
| demo2 | |
| sleep 0.5 | |
| demo3 | |
| sleep 0.5 | |
| # OpenBlinkのリロードチェック | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "Error occurred:" | |
| puts " Class: #{e.class}" | |
| puts " Message: #{e.message}" | |
| # エラーモードでLEDを赤く点滅 | |
| n = 0 | |
| while true do | |
| puts "Error mode: cycle #{n}" | |
| 25.times { |i| LED.set([64, 0, 0], i) } # 赤色で点灯 | |
| sleep 0.5 | |
| 25.times { |i| LED.set([0, 0, 0], i) } # 消灯 | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| n += 1 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix 日本の美しさを表現するLEDデモ | |
| # 日本の伝統的な景色や文化を表現する5つのテーマによるアニメーション | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 # 0-255の範囲に正規化 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # 疑似乱数生成(線形合同法) | |
| def pseudo_random(seed) | |
| (seed * 17 + 23) % 256 | |
| end | |
| # 1. 桜 - 春の桜吹雪をイメージした淡いピンク色のエフェクト | |
| def sakura_blossoms | |
| petals = [] | |
| # 初期の花びらを生成 | |
| 8.times do |i| | |
| # [x, y, 速度, 揺れ位相] | |
| petals << [i % 5, (i / 5 * 2) % 5, (i % 3) + 1, i * 32] | |
| end | |
| phase = 0 | |
| # 約10秒間のアニメーション | |
| 100.times do |time| | |
| # 背景を暗く(春の夜空のような青みがかった黒) | |
| 25.times do |i| | |
| LED.set([1, 1, 3], i) | |
| end | |
| # 桜の花びらを更新し描画 | |
| petals.each_with_index do |petal, i| | |
| x, y, speed, wobble = petal | |
| # 花びらが揺れながら下に落ちる動き | |
| new_y = (y + speed) % 5 | |
| wobble_phase = (wobble + 7) % 256 | |
| wobble_x = (sin_approx(wobble_phase) * 2) / 64 | |
| new_x = (x + wobble_x) % 5 | |
| petals[i] = [new_x, new_y, speed, wobble_phase] | |
| # 桜の色(淡いピンク) | |
| petal_pos = new_y.to_i * 5 + new_x.to_i | |
| # 明るさをsin波で変化させる | |
| brightness = 128 + (sin_approx(time * 5 + i * 32) * 64) / 64 | |
| # 桜色のHSV: 約330-350°, 彩度はやや低め | |
| hue = (342 + sin_approx(time + i * 32) * 10 / 64) % 256 | |
| saturation = 160 + (sin_approx(wobble_phase) * 32) / 64 | |
| rgb = hsv_to_rgb(hue, saturation, brightness) | |
| LED.set(rgb, petal_pos) | |
| # 花びらの軌跡(薄いピンク) | |
| tail_y = (new_y - speed + 5) % 5 | |
| tail_pos = tail_y.to_i * 5 + new_x.to_i | |
| tail_rgb = hsv_to_rgb(hue, saturation / 2, brightness / 3) | |
| LED.set(tail_rgb, tail_pos) | |
| end | |
| # 時々、新しい花びらを生成 | |
| if time % 10 == 0 | |
| seed = phase + time | |
| new_x = seed % 5 | |
| # 花びらは画面上部から降ってくる | |
| petals[time % 8] = [new_x, 0, (seed % 3) + 1, seed * 32] | |
| end | |
| phase = (phase + 1) % 256 | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # 2. 富士山 - 青と白のグラデーションで富士山と日の出を表現 | |
| def fuji_sunrise | |
| # 富士山の形状(5x5のLEDマトリックス上) | |
| fuji_shape = [ | |
| 0, 0, 0, 0, 0, | |
| 0, 0, 1, 0, 0, | |
| 0, 1, 1, 1, 0, | |
| 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1 | |
| ] | |
| # 日の出のアニメーション | |
| 100.times do |time| | |
| sun_pos = 5 - time / 20 # 日の位置(下から上へ) | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 富士山の描画 | |
| if fuji_shape[pos] == 1 | |
| # 山頂の雪は白く、麓は青紫 | |
| snow_line = 3 - (x - 2).abs # 山の形に合わせた雪線 | |
| if y < snow_line | |
| # 山頂(白) | |
| brightness = 200 - (time % 20) * 3 # 日の出に合わせて少しずつ明るく | |
| LED.set([brightness / 4, brightness / 4, brightness / 4], pos) | |
| else | |
| # 山肌(青紫) | |
| mountain_blue = 180 + (sin_approx(time * 3) * 10) / 64 | |
| mountain_hue = (mountain_blue + (y - snow_line) * 5) % 256 | |
| rgb = hsv_to_rgb(mountain_hue, 200, 100 + time / 4) | |
| LED.set(rgb, pos) | |
| end | |
| else | |
| # 空の描画 | |
| if y >= sun_pos && x >= 2 - (y - sun_pos) && x <= 2 + (y - sun_pos) | |
| # 朝日(赤〜黄色) | |
| sun_distance = (x - 2).abs + (y - sun_pos).abs | |
| sun_hue = (20 - sun_distance * 5 + time / 5) % 256 | |
| sun_brightness = 255 - sun_distance * 20 | |
| rgb = hsv_to_rgb(sun_hue, 255, sun_brightness) | |
| LED.set(rgb, pos) | |
| else | |
| # 空(日の出の進行で徐々に青く) | |
| sky_time = time / 2 | |
| if sky_time > 40 | |
| sky_time = 40 | |
| end | |
| sky_blue = 150 + (sin_approx(time) * 10) / 64 | |
| sky_brightness = 50 + sky_time * 3 | |
| rgb = hsv_to_rgb(sky_blue, 200, sky_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| end | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # 3. 提灯 - 和風の灯りを表現 | |
| def chochin_light | |
| time = 0 | |
| # 提灯の形状(5x5のLEDマトリックス上) | |
| chochin_shape = [ | |
| 0, 1, 1, 1, 0, | |
| 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, | |
| 0, 1, 0, 1, 0 | |
| ] | |
| # 約10秒間のアニメーション | |
| 100.times do | |
| pseudo_random_val = pseudo_random(time) | |
| flicker = (sin_approx(time * 8) * 40) / 64 # 炎のようなゆらぎ効果 | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| if chochin_shape[pos] == 1 | |
| # 提灯の内側(暖かい光) | |
| if pos % 7 == time % 7 | |
| # 時々明るく光る(キラキラ効果) | |
| brightness = 200 + flicker | |
| hue = (25 + sin_approx(time + pos) * 5 / 64) % 256 # 黄色〜橙色 | |
| saturation = 180 | |
| else | |
| # 通常の提灯の明かり | |
| brightness = 160 + flicker | |
| hue = (30 + sin_approx(time * 2 + pos * 3) * 10 / 64) % 256 # 橙色〜赤色 | |
| saturation = 220 | |
| end | |
| rgb = hsv_to_rgb(hue, saturation, brightness) | |
| LED.set(rgb, pos) | |
| else | |
| # 提灯の外側(暗い背景) | |
| dark_blue = 170 # 夜の青 | |
| rgb = hsv_to_rgb(dark_blue, 200, 40 + (flicker / 4)) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| time += 1 | |
| # 時々大きなゆらぎを入れる(風が吹いた感じ) | |
| if pseudo_random_val < 30 # 約12%の確率 | |
| sleep 0.15 | |
| else | |
| sleep 0.1 | |
| end | |
| end | |
| end | |
| # 4. 紅葉 - 秋の風景を赤と金色で表現 | |
| def momiji_autumn | |
| leaves = [] | |
| 10.times do |i| | |
| # [x, y, 色相, 位相] | |
| leaves << [i % 5, (i / 2) % 5, 0 + (i * 7), i * 25] | |
| end | |
| wind_phase = 0 | |
| # 約10秒間のアニメーション | |
| 100.times do |time| | |
| # 森の背景を暗い緑で描画 | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(100, 180, 40) # 深緑 | |
| LED.set(rgb, i) | |
| end | |
| # 紅葉の葉を描画 | |
| leaves.each_with_index do |leaf, i| | |
| x, y, hue_base, phase = leaf | |
| # 風で揺れる効果 | |
| wind = (sin_approx(wind_phase + phase) * 2) / 64 | |
| new_x = (x + wind + 5) % 5 | |
| # 葉が徐々に散る | |
| if time % 20 == i % 20 | |
| new_y = (y + 1) % 5 | |
| else | |
| new_y = y | |
| end | |
| # 紅葉の色相を変化(赤~黄色) | |
| hue_shift = (sin_approx(time + i * 10) * 20) / 64 | |
| hue = (hue_base + hue_shift) % 256 | |
| # 季節に応じた色変化(赤→黄→茶色) | |
| if i % 3 == 0 | |
| leaf_hue = (0 + hue) % 256 # 赤 | |
| saturation = 255 | |
| elsif i % 3 == 1 | |
| leaf_hue = (30 + hue) % 256 # 橙 | |
| saturation = 240 | |
| else | |
| leaf_hue = (40 + hue) % 256 # 黄 | |
| saturation = 230 | |
| end | |
| brightness = 180 + (sin_approx(phase + time * 5) * 50) / 64 | |
| rgb = hsv_to_rgb(leaf_hue, saturation, brightness) | |
| # 葉を描画 | |
| pos = new_y.to_i * 5 + new_x.to_i | |
| LED.set(rgb, pos) | |
| # 葉の状態を更新 | |
| leaves[i] = [new_x, new_y, hue_base, phase] | |
| end | |
| wind_phase = (wind_phase + 3) % 256 | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # 5. 禅庭 - 静けさと調和を表現するミニマルなパターン | |
| def zen_garden | |
| # 砂紋の位相 | |
| wave_phase = 0 | |
| stone_positions = [7, 17] # 石の位置 | |
| # 約10秒間のアニメーション | |
| 100.times do |time| | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 石の描画 | |
| if stone_positions.include?(pos) | |
| # 石は静的なグレー | |
| stone_brightness = 100 + (sin_approx(time) * 20) / 64 | |
| LED.set([stone_brightness / 4, stone_brightness / 4, stone_brightness / 4], pos) | |
| else | |
| # 砂紋の描画 | |
| distance_from_stone1 = (x - (stone_positions[0] % 5)).abs + (y - (stone_positions[0] / 5)).abs | |
| distance_from_stone2 = (x - (stone_positions[1] % 5)).abs + (y - (stone_positions[1] / 5)).abs | |
| min_distance = distance_from_stone1 < distance_from_stone2 ? distance_from_stone1 : distance_from_stone2 | |
| # 波紋効果 | |
| wave_value = (sin_approx(min_distance * 32 + wave_phase) * 30) / 64 | |
| # 砂の色(明るいベージュ) | |
| sand_brightness = 120 + wave_value | |
| sand_hue = 45 # ベージュの色相 | |
| sand_saturation = 40 # 低い彩度 | |
| rgb = hsv_to_rgb(sand_hue, sand_saturation, sand_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| # 非常にゆっくりとした波紋の動き(禅の静けさを表現) | |
| if time % 5 == 0 | |
| wave_phase = (wave_phase + 1) % 256 | |
| end | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # 6. 花火 - 夏祭りの夜空を彩る花火 | |
| def hanabi_festival | |
| sky_phase = 0 | |
| fireworks = [] | |
| # 約10秒間のアニメーション | |
| 100.times do |time| | |
| # 夜空の背景 | |
| 25.times do |i| | |
| LED.set([1, 1, 5], i) # 暗い青紫色の夜空 | |
| end | |
| # 時々新しい花火を発射 | |
| if time % 15 == 0 || fireworks.empty? | |
| x = (time * 17 + 23) % 5 | |
| y = 4 # 画面下部から発射 | |
| hue = (time * 71) % 256 # ランダムな色 | |
| fireworks << [x, y, 0, hue] # [x, y, stage, hue] | |
| end | |
| # 花火の更新と描画 | |
| new_fireworks = [] | |
| fireworks.each do |fw| | |
| x, y, stage, hue = fw | |
| if stage < 5 # 上昇中 | |
| # 花火が上昇する軌跡 | |
| new_y = y - 1 | |
| if new_y >= 0 | |
| pos = new_y * 5 + x | |
| brightness = 100 + stage * 30 | |
| rgb = hsv_to_rgb(hue, 100, brightness) | |
| LED.set(rgb, pos) | |
| # 軌跡を残す | |
| if new_y < 4 | |
| tail_pos = (new_y + 1) * 5 + x | |
| tail_rgb = hsv_to_rgb(hue, 50, 50) | |
| LED.set(tail_rgb, tail_pos) | |
| end | |
| new_fireworks << [x, new_y, stage + 1, hue] | |
| else | |
| # 画面上部に達したら爆発 | |
| new_fireworks << [x, 0, 5, hue] | |
| end | |
| elsif stage < 15 # 爆発中 | |
| center_x = x | |
| center_y = (stage - 5) / 3 # 爆発の中心位置 | |
| # 爆発のパターンを描画 | |
| explosion_size = (stage - 4) / 2 | |
| 5.times do |ey| | |
| 5.times do |ex| | |
| distance = (ex - center_x).abs + (ey - center_y).abs | |
| if distance <= explosion_size | |
| pos = ey * 5 + ex | |
| # 爆発の中心ほど明るく | |
| brightness = 255 - distance * 40 - (stage - 5) * 10 | |
| if brightness > 0 | |
| explosion_hue = (hue + distance * 10) % 256 | |
| rgb = hsv_to_rgb(explosion_hue, 255, brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| end | |
| if stage < 14 # まだ爆発中なら続ける | |
| new_fireworks << [x, y, stage + 1, hue] | |
| end | |
| end | |
| end | |
| fireworks = new_fireworks | |
| sky_phase = (sky_phase + 1) % 256 | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # 7. 水面 - 日本庭園の池の静かな水面を表現 | |
| def mizuumi_reflection | |
| ripple_phase = 0 | |
| koi_pos = [12, 7] # 錦鯉の位置 | |
| koi_direction = [1, -1] # 錦鯉の移動方向 | |
| # 約10秒間のアニメーション | |
| 100.times do |time| | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 水面の基本色(青緑) | |
| water_hue = 190 | |
| # 水面の波紋効果 | |
| ripple = (sin_approx(x * 20 + y * 30 + ripple_phase) * 15) / 64 | |
| brightness = 100 + ripple | |
| # 水の深さによる色の変化 | |
| depth_hue = (water_hue - y * 5) % 256 | |
| # 月光の反射 | |
| reflection_x = 2 | |
| reflection_y = 1 | |
| distance_from_reflection = (x - reflection_x).abs + (y - reflection_y).abs | |
| if distance_from_reflection < 2 | |
| brightness = 150 - distance_from_reflection * 30 + ripple | |
| saturation = 100 | |
| else | |
| saturation = 200 | |
| end | |
| rgb = hsv_to_rgb(depth_hue, saturation, brightness) | |
| LED.set(rgb, pos) | |
| # 錦鯉の描画 | |
| koi_x = koi_pos[0] % 5 | |
| koi_y = koi_pos[0] / 5 | |
| koi_x2 = koi_pos[1] % 5 | |
| koi_y2 = koi_pos[1] / 5 | |
| if (x == koi_x && y == koi_y) || (x == koi_x2 && y == koi_y2) | |
| # 錦鯉の色(赤と白) | |
| if pos % 2 == 0 | |
| koi_rgb = hsv_to_rgb(0, 255, 200) # 赤 | |
| else | |
| koi_rgb = hsv_to_rgb(0, 0, 200) # 白 | |
| end | |
| LED.set(koi_rgb, pos) | |
| end | |
| end | |
| end | |
| # 錦鯉の移動 | |
| if time % 3 == 0 | |
| koi_pos[0] = (koi_pos[0] + koi_direction[0] + 25) % 25 | |
| if time % 10 == 0 | |
| koi_direction[0] = -koi_direction[0] # 方向転換 | |
| end | |
| end | |
| if time % 4 == 0 | |
| koi_pos[1] = (koi_pos[1] + koi_direction[1] + 25) % 25 | |
| if time % 12 == 0 | |
| koi_direction[1] = -koi_direction[1] # 方向転換 | |
| end | |
| end | |
| ripple_phase = (ripple_phase + 1) % 256 | |
| sleep 0.1 # 10秒で約100フレーム | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| puts "桜のデモを開始" | |
| sakura_blossoms # 桜 | |
| sleep 0.5 | |
| puts "富士山と日の出のデモを開始" | |
| fuji_sunrise # 富士山と日の出 | |
| sleep 0.5 | |
| puts "提灯のデモを開始" | |
| chochin_light # 提灯の灯り | |
| sleep 0.5 | |
| puts "紅葉のデモを開始" | |
| momiji_autumn # 紅葉 | |
| sleep 0.5 | |
| puts "禅庭のデモを開始" | |
| zen_garden # 禅庭 | |
| sleep 0.5 | |
| puts "花火のデモを開始" | |
| hanabi_festival # 花火 | |
| sleep 0.5 | |
| puts "水面のデモを開始" | |
| mizuumi_reflection # 水面 | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラー表示(日の丸風) | |
| error_count = 0 | |
| while true do | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| center_distance = (x - 2).abs + (y - 2).abs | |
| if center_distance < 2 | |
| LED.set([64, 0, 0], i) # 赤(日の丸) | |
| else | |
| LED.set([32, 32, 32], i) # 白(背景) | |
| end | |
| end | |
| sleep 0.5 | |
| 25.times do |i| | |
| LED.set([0, 0, 0], i) # 全消灯 | |
| end | |
| sleep 0.5 | |
| error_count += 1 | |
| break if Blink.req_reload? || error_count > 5 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix マインクラフトデモ | |
| # ブロックの世界観を5x5マトリックスで表現 | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # | |
| # 使用できない機能: | |
| # - Math モジュール(sin, cos など) | |
| # - rand - 乱数生成 | |
| # - LED.get - 現在のLED色の取得 | |
| # - respond_to? - メソッド存在チェック | |
| # - reverse, reverse_each - 配列の逆順処理 | |
| # - 浮動小数点数の%演算 | |
| # - backtrace - エラーのスタックトレース | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 # 0-255の範囲に正規化 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # 疑似乱数生成(線形合同法) | |
| def pseudo_random(seed) | |
| (seed * 17 + 23) % 256 | |
| end | |
| # 1. マインクラフトの日夜サイクル | |
| def day_night_cycle | |
| # 一日のサイクル(朝→昼→夕方→夜→朝) | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| count = 0 | |
| while count < 80 | |
| # 時間帯に応じて空の色を変更 | |
| sky_hue = 0 | |
| sky_sat = 0 | |
| sky_bright = 0 | |
| cycle_pos = time % 80 # 80フレームを一日とする | |
| if cycle_pos < 20 # 朝 | |
| sky_hue = 200 - cycle_pos # 青から水色へ | |
| sky_sat = 100 + cycle_pos * 2 | |
| sky_bright = 100 + cycle_pos * 3 | |
| elsif cycle_pos < 40 # 昼 | |
| sky_hue = 180 | |
| sky_sat = 140 | |
| sky_bright = 160 | |
| elsif cycle_pos < 60 # 夕方 | |
| sky_hue = 30 - (cycle_pos - 40) # オレンジから赤へ | |
| sky_sat = 200 | |
| sky_bright = 160 - (cycle_pos - 40) * 3 | |
| else # 夜 | |
| sky_hue = 240 | |
| sky_sat = 180 | |
| sky_bright = 40 + (sin_approx(time * 8) * 10) / 64 # 星のきらめき | |
| end | |
| # 空を描画 | |
| 2.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 空の色 | |
| rgb = hsv_to_rgb(sky_hue, sky_sat, sky_bright) | |
| LED.set(rgb, pos) | |
| # 夜には星を表示 | |
| if cycle_pos >= 60 && pseudo_random(pos + time) < 20 && y == 0 | |
| LED.set([16, 16, 16], pos) # 星 | |
| end | |
| end | |
| end | |
| # 地形を描画(3段目以降) | |
| 3.times do |y| | |
| real_y = y + 2 # 3,4,5段目 | |
| 5.times do |x| | |
| pos = real_y * 5 + x | |
| # 地形の種類を決定 | |
| terrain_type = (pseudo_random(x * 10 + real_y * 5) % 4) | |
| case terrain_type | |
| when 0 # 草ブロック | |
| if real_y == 2 | |
| rgb = cycle_pos >= 60 ? [0, 3, 0] : [0, 16, 0] # 夜は暗く | |
| LED.set(rgb, pos) | |
| else | |
| # 土 | |
| rgb = cycle_pos >= 60 ? [3, 2, 0] : [16, 10, 0] # 夜は暗く | |
| LED.set(rgb, pos) | |
| end | |
| when 1 # 石 | |
| rgb = cycle_pos >= 60 ? [3, 3, 3] : [14, 14, 14] # 夜は暗く | |
| LED.set(rgb, pos) | |
| when 2 # 砂 | |
| rgb = cycle_pos >= 60 ? [5, 5, 0] : [24, 24, 0] # 夜は暗く | |
| LED.set(rgb, pos) | |
| when 3 # 水 | |
| water_bright = 80 + (sin_approx(time * 10 + x * 20) * 20) / 64 | |
| water_bright = water_bright / 3 if cycle_pos >= 60 # 夜は暗く | |
| rgb = hsv_to_rgb(190, 200, water_bright) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 2. ブロック採掘アニメーション | |
| def mining_blocks | |
| # ブロックの配置 | |
| blocks = [] | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| # [x, y, タイプ, 耐久度] | |
| blocks[i] = [x, y, pseudo_random(i) % 5, 3] | |
| end | |
| time = 0 | |
| pickaxe_pos = 12 # 中央からスタート | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # すべてのブロックを描画 | |
| 25.times do |i| | |
| block = blocks[i] | |
| block_type = block[2] | |
| durability = block[3] | |
| if durability > 0 | |
| # ブロックの種類ごとに色を設定 | |
| case block_type | |
| when 0 # 石 | |
| rgb = [12, 12, 12] | |
| when 1 # 石炭 | |
| rgb = [5, 5, 5] | |
| when 2 # 鉄 | |
| rgb = [24, 18, 16] | |
| when 3 # 金 | |
| rgb = [24, 24, 0] | |
| when 4 # ダイヤモンド | |
| rgb = [0, 24, 24] | |
| end | |
| # 採掘中は点滅 | |
| if i == pickaxe_pos && time % 6 < 3 | |
| LED.set([24, 24, 24], i) # 白く点滅 | |
| else | |
| LED.set(rgb, i) | |
| end | |
| else | |
| # 採掘済みは暗く | |
| LED.set([1, 1, 1], i) | |
| end | |
| end | |
| # ツルハシの移動と採掘 | |
| if time % 15 == 0 | |
| # ツルハシの移動 | |
| move_dir = pseudo_random(time) % 4 | |
| old_x = pickaxe_pos % 5 | |
| old_y = pickaxe_pos / 5 | |
| case move_dir | |
| when 0 # 上 | |
| new_y = (old_y - 1 + 5) % 5 | |
| new_x = old_x | |
| when 1 # 右 | |
| new_y = old_y | |
| new_x = (old_x + 1) % 5 | |
| when 2 # 下 | |
| new_y = (old_y + 1) % 5 | |
| new_x = old_x | |
| when 3 # 左 | |
| new_y = old_y | |
| new_x = (old_x - 1 + 5) % 5 | |
| end | |
| pickaxe_pos = new_y * 5 + new_x | |
| end | |
| # 採掘アクション | |
| if time % 6 == 0 | |
| block = blocks[pickaxe_pos] | |
| if block[3] > 0 | |
| block[3] -= 1 | |
| # 採掘完了時のエフェクト | |
| if block[3] == 0 | |
| 5.times do | |
| LED.set([24, 24, 24], pickaxe_pos) # 白く光る | |
| sleep 0.05 | |
| case block[2] | |
| when 0 # 石 | |
| rgb = [12, 12, 12] | |
| when 1 # 石炭 | |
| rgb = [5, 5, 5] | |
| when 2 # 鉄 | |
| rgb = [24, 18, 16] | |
| when 3 # 金 | |
| rgb = [24, 24, 0] | |
| when 4 # ダイヤモンド | |
| rgb = [0, 24, 24] | |
| end | |
| LED.set(rgb, pickaxe_pos) | |
| sleep 0.05 | |
| end | |
| end | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 3. クリーパーのアニメーション | |
| def creeper_animation | |
| # クリーパーの顔のパターン | |
| face = [ | |
| 0, 0, 0, 0, 0, | |
| 0, 1, 0, 1, 0, | |
| 0, 0, 0, 0, 0, | |
| 0, 1, 1, 1, 0, | |
| 0, 1, 0, 1, 0 | |
| ] | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| count = 0 | |
| while count < 80 | |
| # 背景(夜の空) | |
| 25.times do |i| | |
| LED.set([0, 0, 4], i) # 暗い青 | |
| end | |
| # クリーパーの表示 | |
| 25.times do |i| | |
| if face[i] == 1 | |
| # 通常状態 | |
| LED.set([0, 16, 0], i) # 緑色 | |
| end | |
| end | |
| # クリーパーがシューッと音を立てる | |
| if time % 40 > 30 | |
| hiss_intensity = (time % 40) - 30 # 0-9の強度 | |
| # クリーパーが白く点滅(爆発の前兆) | |
| if time % 2 == 0 | |
| 25.times do |i| | |
| if face[i] == 1 | |
| # 点滅強度を増加 | |
| bright = 16 + hiss_intensity * 4 | |
| bright = bright > 64 ? 64 : bright | |
| LED.set([bright, bright, 0], i) # 黄色っぽく | |
| end | |
| end | |
| else | |
| 25.times do |i| | |
| if face[i] == 1 | |
| LED.set([0, 16, 0], i) # 通常の緑色 | |
| end | |
| end | |
| end | |
| end | |
| # 爆発アニメーション | |
| if time % 40 == 39 | |
| # 爆発の3段階 | |
| 3.times do |stage| | |
| # 爆発の広がり | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| dx = (x - 2).abs | |
| dy = (y - 2).abs | |
| distance = dx + dy # マンハッタン距離 | |
| if distance <= stage | |
| rgb = [64, 48, 0] # 爆発の色 | |
| else | |
| rgb = [0, 0, 4] # 背景 | |
| end | |
| LED.set(rgb, i) | |
| end | |
| sleep 0.1 | |
| end | |
| # 爆発の後、すべて消える | |
| 25.times do |i| | |
| LED.set([0, 0, 0], i) | |
| end | |
| sleep 0.5 | |
| # 再び暗い背景に戻る | |
| 25.times do |i| | |
| LED.set([0, 0, 4], i) | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 4. ブロック建設アニメーション | |
| def building_blocks | |
| # 空のグリッド | |
| grid = [] | |
| 25.times do |i| | |
| grid[i] = 0 # 0=空、1-6=ブロックの種類 | |
| end | |
| time = 0 | |
| cursor_pos = 12 # 中央からスタート | |
| current_block = 1 # 最初のブロックタイプ | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 空の背景 | |
| 25.times do |i| | |
| if grid[i] == 0 | |
| # 空(水色) | |
| LED.set([0, 8, 16], i) | |
| else | |
| # 設置されたブロック | |
| case grid[i] | |
| when 1 # 木材 | |
| rgb = [12, 6, 0] | |
| when 2 # 石 | |
| rgb = [12, 12, 12] | |
| when 3 # レンガ | |
| rgb = [16, 8, 8] | |
| when 4 # ガラス | |
| rgb = [16, 16, 24] | |
| when 5 # 赤石 | |
| rgb = [24, 0, 0] | |
| when 6 # ラピスラズリ | |
| rgb = [0, 0, 24] | |
| end | |
| LED.set(rgb, i) | |
| end | |
| end | |
| # カーソルの描画 | |
| led_state = grid[cursor_pos] | |
| # カーソル位置を点滅 | |
| if time % 6 < 3 | |
| case current_block | |
| when 1 # 木材 | |
| rgb = [20, 10, 0] | |
| when 2 # 石 | |
| rgb = [20, 20, 20] | |
| when 3 # レンガ | |
| rgb = [24, 12, 12] | |
| when 4 # ガラス | |
| rgb = [24, 24, 32] | |
| when 5 # 赤石 | |
| rgb = [32, 0, 0] | |
| when 6 # ラピスラズリ | |
| rgb = [0, 0, 32] | |
| end | |
| else | |
| if led_state == 0 | |
| # 空 | |
| rgb = [0, 8, 16] | |
| else | |
| # 既に設置されたブロック | |
| case led_state | |
| when 1 then rgb = [12, 6, 0] | |
| when 2 then rgb = [12, 12, 12] | |
| when 3 then rgb = [16, 8, 8] | |
| when 4 then rgb = [16, 16, 24] | |
| when 5 then rgb = [24, 0, 0] | |
| when 6 then rgb = [0, 0, 24] | |
| end | |
| end | |
| end | |
| LED.set(rgb, cursor_pos) | |
| # カーソル移動と操作 | |
| if time % 10 == 0 | |
| # カーソル移動 | |
| move_dir = pseudo_random(time) % 4 | |
| old_x = cursor_pos % 5 | |
| old_y = cursor_pos / 5 | |
| case move_dir | |
| when 0 # 上 | |
| new_y = (old_y - 1 + 5) % 5 | |
| new_x = old_x | |
| when 1 # 右 | |
| new_y = old_y | |
| new_x = (old_x + 1) % 5 | |
| when 2 # 下 | |
| new_y = (old_y + 1) % 5 | |
| new_x = old_x | |
| when 3 # 左 | |
| new_y = old_y | |
| new_x = (old_x - 1 + 5) % 5 | |
| end | |
| cursor_pos = new_y * 5 + new_x | |
| end | |
| # ブロック設置/破壊 | |
| if time % 20 == 0 | |
| if grid[cursor_pos] == 0 | |
| # ブロック設置 | |
| grid[cursor_pos] = current_block | |
| # 設置エフェクト | |
| 3.times do | |
| LED.set([32, 32, 32], cursor_pos) | |
| sleep 0.05 | |
| case current_block | |
| when 1 then rgb = [12, 6, 0] | |
| when 2 then rgb = [12, 12, 12] | |
| when 3 then rgb = [16, 8, 8] | |
| when 4 then rgb = [16, 16, 24] | |
| when 5 then rgb = [24, 0, 0] | |
| when 6 then rgb = [0, 0, 24] | |
| end | |
| LED.set(rgb, cursor_pos) | |
| sleep 0.05 | |
| end | |
| # ブロックタイプを変更 | |
| current_block = current_block % 6 + 1 | |
| else | |
| # ブロック破壊 | |
| 3.times do | |
| LED.set([32, 32, 32], cursor_pos) | |
| sleep 0.05 | |
| case grid[cursor_pos] | |
| when 1 then rgb = [12, 6, 0] | |
| when 2 then rgb = [12, 12, 12] | |
| when 3 then rgb = [16, 8, 8] | |
| when 4 then rgb = [16, 16, 24] | |
| when 5 then rgb = [24, 0, 0] | |
| when 6 then rgb = [0, 0, 24] | |
| end | |
| LED.set(rgb, cursor_pos) | |
| sleep 0.05 | |
| end | |
| grid[cursor_pos] = 0 # ブロックを空に | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 5. ミニマップモード | |
| def minimap_mode | |
| # 地形のミニマップを表現 | |
| map = [ | |
| 1, 1, 1, 2, 2, # 1=草原, 2=山 | |
| 1, 1, 2, 2, 3, # 3=水 | |
| 1, 2, 2, 3, 3, | |
| 1, 2, 3, 3, 4, # 4=砂漠 | |
| 5, 5, 4, 4, 4 # 5=森 | |
| ] | |
| player_x = 0 | |
| player_y = 0 | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| count = 0 | |
| while count < 80 | |
| # 地図を描画 | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| terrain = map[i] | |
| # プレイヤー位置は点滅 | |
| if x == player_x && y == player_y | |
| if time % 6 < 3 | |
| LED.set([32, 32, 32], i) | |
| next | |
| end | |
| end | |
| # 地形ごとに色を設定 | |
| case terrain | |
| when 1 # 草原 | |
| rgb = [0, 16, 0] | |
| when 2 # 山 | |
| rgb = [12, 12, 12] | |
| when 3 # 水 | |
| bright = 80 + (sin_approx(time * 10 + i) * 20) / 64 | |
| rgb = hsv_to_rgb(190, 200, bright) | |
| when 4 # 砂漠 | |
| rgb = [24, 24, 0] | |
| when 5 # 森 | |
| rgb = [0, 8, 0] | |
| end | |
| LED.set(rgb, i) | |
| end | |
| # プレイヤーの移動 | |
| if time % 10 == 0 | |
| move_dir = pseudo_random(time) % 4 | |
| case move_dir | |
| when 0 # 上 | |
| player_y = (player_y - 1 + 5) % 5 | |
| when 1 # 右 | |
| player_x = (player_x + 1) % 5 | |
| when 2 # 下 | |
| player_y = (player_y + 1) % 5 | |
| when 3 # 左 | |
| player_x = (player_x - 1 + 5) % 5 | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| puts "マインクラフト日夜サイクルデモを開始" | |
| day_night_cycle | |
| sleep 0.5 | |
| puts "ブロック採掘デモを開始" | |
| mining_blocks | |
| sleep 0.5 | |
| puts "クリーパーアニメーションデモを開始" | |
| creeper_animation | |
| sleep 0.5 | |
| puts "ブロック建設デモを開始" | |
| building_blocks | |
| sleep 0.5 | |
| puts "ミニマップモードデモを開始" | |
| minimap_mode | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # マインクラフトのエラー画面(赤い背景) | |
| while true do | |
| 25.times do |i| | |
| LED.set([16, 0, 0], i) | |
| end | |
| sleep 0.5 | |
| # "JAVA"という文字をドット絵で表現 | |
| [1, 2, 3, 5, 6, 8, 11, 13, 16, 17, 18, 21, 23].each do |i| | |
| LED.set([32, 32, 32], i) | |
| end | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix 海の生き物デモ | |
| # 海洋生物と海中の光の表現 | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - シンプルな配列操作(配列アクセス、push) | |
| # - 条件分岐(if, case) | |
| # - 単純なループ(times, while) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # | |
| # 使用できない機能: | |
| # - 配列の分解代入 |(a, b), i| → 代わりにインデックスアクセスを使用 | |
| # - each, each_with_index などの高度なイテレータ | |
| # - .abs などの標準ライブラリメソッド | |
| # - LED.get - 現在のLED色の取得 | |
| # - rand - 乱数生成 | |
| # - Math モジュール(sin, cos など) | |
| # - 浮動小数点数の%演算 | |
| # - 複雑なブロック引数(any?などのブロック内で複雑な条件を使用) | |
| # - 三項演算子(条件 ? 真の場合 : 偽の場合) | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| if s > 255 | |
| s = 255 | |
| elsif s < 0 | |
| s = 0 | |
| end | |
| if v > 255 | |
| v = 255 | |
| elsif v < 0 | |
| v = 0 | |
| end | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| # .absを使わずに絶対値計算 | |
| h_mod_diff = (h_mod * 2) - 255 | |
| if h_mod_diff < 0 | |
| h_mod_diff = -h_mod_diff | |
| end | |
| x = (c * (255 - h_mod_diff)) / 255 | |
| m = v - c | |
| r = 0 | |
| g = 0 | |
| b = 0 | |
| case h_section | |
| when 0 | |
| r = c | |
| g = x | |
| b = 0 | |
| when 1 | |
| r = x | |
| g = c | |
| b = 0 | |
| when 2 | |
| r = 0 | |
| g = c | |
| b = x | |
| when 3 | |
| r = 0 | |
| g = x | |
| b = c | |
| when 4 | |
| r = x | |
| g = 0 | |
| b = c | |
| when 5 | |
| r = c | |
| g = 0 | |
| b = x | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 # 0-255の範囲に正規化 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # 疑似乱数生成(線形合同法) | |
| def pseudo_random(seed) | |
| (seed * 17 + 23) % 256 | |
| end | |
| # 絶対値を計算(.absを使わずに) | |
| def abs_val(num) | |
| if num < 0 | |
| -num | |
| else | |
| num | |
| end | |
| end | |
| # 1. 熱帯魚の群れ - カラフルな魚が泳ぐ様子 | |
| def tropical_fish_school | |
| fish = [] | |
| # 初期の魚を生成 | |
| i = 0 | |
| while i < 6 | |
| # [x, y, 色相, 方向, 速度] | |
| fish[i] = [i % 5, (i / 5) % 5, i * 36, i % 4, (i % 2) + 1] | |
| i += 1 | |
| end | |
| time = 0 | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 海の背景(青緑色の濃淡) | |
| i = 0 | |
| while i < 25 | |
| x = i % 5 | |
| y = i / 5 | |
| depth = y + 1 # 深さによる色の変化 | |
| water_hue = 185 - depth * 5 # 深いほど青みが強くなる | |
| water_brightness = 100 - depth * 10 # 深いほど暗くなる | |
| # 水中の光の屈折効果 | |
| light_effect = (sin_approx(time * 2 + x * 30 + y * 20) * 15) / 64 | |
| rgb = hsv_to_rgb(water_hue, 180, water_brightness + light_effect) | |
| LED.set(rgb, i) | |
| i += 1 | |
| end | |
| # 魚を更新・描画 | |
| idx = 0 | |
| while idx < fish.size | |
| f = fish[idx] | |
| x = f[0] | |
| y = f[1] | |
| hue = f[2] | |
| direction = f[3] | |
| speed = f[4] | |
| # 魚の動きを計算 | |
| new_x = x | |
| new_y = y | |
| case direction | |
| when 0 # 右 | |
| new_x = (x + speed) % 5 | |
| when 1 # 下 | |
| new_y = (y + speed) % 5 | |
| when 2 # 左 | |
| new_x = (x - speed + 5) % 5 | |
| when 3 # 上 | |
| new_y = (y - speed + 5) % 5 | |
| end | |
| # 時々方向転換 | |
| if time % 10 == idx % 10 | |
| direction = (direction + 1 + (time % 3)) % 4 | |
| end | |
| # 魚の色を更新(わずかに変化) | |
| new_hue = (hue + 1) % 256 | |
| # 魚の描画 | |
| pos = new_y * 5 + new_x | |
| brightness = 180 + (sin_approx(time * 8 + idx * 32) * 50) / 64 # キラキラ効果 | |
| rgb = hsv_to_rgb(new_hue, 240, brightness) | |
| LED.set(rgb, pos) | |
| # 魚の尾(動きの方向に描画) | |
| tail_x = new_x | |
| tail_y = new_y | |
| if direction == 0 | |
| tail_x = (new_x - 1 + 5) % 5 | |
| elsif direction == 2 | |
| tail_x = (new_x + 1) % 5 | |
| end | |
| if direction == 1 | |
| tail_y = (new_y - 1 + 5) % 5 | |
| elsif direction == 3 | |
| tail_y = (new_y + 1) % 5 | |
| end | |
| tail_pos = tail_y * 5 + tail_x | |
| # 尾は本体より暗く | |
| tail_rgb = hsv_to_rgb(new_hue, 200, brightness / 2) | |
| LED.set(tail_rgb, tail_pos) | |
| # 魚の状態を更新 | |
| fish[idx] = [new_x, new_y, new_hue, direction, speed] | |
| idx += 1 | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 2. クラゲの脈動 - ゆっくり浮遊するクラゲの表現 | |
| def jellyfish_pulsation | |
| jellies = [] | |
| # 初期のクラゲを生成 | |
| i = 0 | |
| while i < 3 | |
| # [x, y, サイズ, 脈動位相] | |
| jellies[i] = [1 + i, 4, 1, i * 64] | |
| i += 1 | |
| end | |
| time = 0 | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 深い海の背景(暗い青) | |
| i = 0 | |
| while i < 25 | |
| LED.set([0, 2, 4], i) | |
| i += 1 | |
| end | |
| # クラゲを更新・描画 | |
| idx = 0 | |
| while idx < jellies.size | |
| j = jellies[idx] | |
| x = j[0] | |
| y = j[1] | |
| size = j[2] | |
| pulse_phase = j[3] | |
| # 脈動による上下運動 | |
| pulse = sin_approx(pulse_phase) | |
| new_y = (y - (pulse / 128)) % 5 | |
| # 左右の揺れ | |
| wobble = (sin_approx(pulse_phase / 2) * 1) / 64 | |
| new_x = (x + wobble + 5) % 5 | |
| # クラゲの大きさは脈動とともに変化 | |
| new_size = 1 + (pulse / 128) | |
| # クラゲの体を描画 | |
| center_pos = new_y.to_i * 5 + new_x.to_i | |
| jelly_hue = (180 + idx * 25 + sin_approx(time * 2) * 5 / 64) % 256 # 青紫〜ピンク | |
| # 透明感のある色 | |
| transparency = 128 + (sin_approx(pulse_phase) * 64) / 64 | |
| # クラゲの体 | |
| rgb = hsv_to_rgb(jelly_hue, 150, transparency) | |
| LED.set(rgb, center_pos) | |
| # クラゲの触手 | |
| if new_y < 4 | |
| tentacle_pos = (new_y.to_i + 1) * 5 + new_x.to_i | |
| tentacle_rgb = hsv_to_rgb(jelly_hue, 100, transparency / 2) | |
| LED.set(tentacle_rgb, tentacle_pos) | |
| end | |
| # 大きいクラゲは周囲も光る | |
| if new_size > 1 | |
| # 配列の分解代入を使わずに記述 | |
| offset_idx = 0 | |
| offsets = [[-1,0], [1,0], [0,-1]] | |
| while offset_idx < offsets.size | |
| offset = offsets[offset_idx] | |
| dx = offset[0] | |
| dy = offset[1] | |
| nx = (new_x.to_i + dx + 5) % 5 | |
| ny = (new_y.to_i + dy + 5) % 5 | |
| if nx >= 0 && nx < 5 && ny >= 0 && ny < 5 | |
| edge_pos = ny * 5 + nx | |
| edge_rgb = hsv_to_rgb(jelly_hue, 100, transparency / 3) | |
| LED.set(edge_rgb, edge_pos) | |
| end | |
| offset_idx += 1 | |
| end | |
| end | |
| # クラゲのバイオルミネセンス効果(時々光る) | |
| if pulse_phase % 100 < 10 | |
| glow_rgb = hsv_to_rgb(jelly_hue, 50, 255) | |
| LED.set(glow_rgb, center_pos) | |
| end | |
| # クラゲの状態を更新 | |
| new_pulse_phase = (pulse_phase + 3) % 256 | |
| jellies[idx] = [new_x, new_y, new_size, new_pulse_phase] | |
| idx += 1 | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 3. タコの擬態 - 色を変えるタコの表現 | |
| def octopus_camouflage | |
| # タコの位置と状態 | |
| octopus_x = 2 | |
| octopus_y = 2 | |
| octopus_state = 0 # 0=通常, 1=隠れる, 2=逃げる | |
| color_phase = 0 | |
| # 障害物(岩や珊瑚)の位置 | |
| obstacles = [] | |
| obstacles[0] = [0, 0] | |
| obstacles[1] = [4, 0] | |
| obstacles[2] = [0, 4] | |
| obstacles[3] = [4, 4] | |
| time = 0 | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 海底の背景(砂地と岩) | |
| i = 0 | |
| while i < 25 | |
| x = i % 5 | |
| y = i / 5 | |
| # 障害物(岩や珊瑚)- any?メソッドを使わずに検出 | |
| is_obstacle = false | |
| obstacle_idx = 0 | |
| while obstacle_idx < 4 # 障害物の数は4つと固定 | |
| ox = obstacles[obstacle_idx][0] | |
| oy = obstacles[obstacle_idx][1] | |
| if ox == x && oy == y | |
| is_obstacle = true | |
| break | |
| end | |
| obstacle_idx += 1 | |
| end | |
| if is_obstacle | |
| rock_hue = (25 + sin_approx(time * 3 + i) * 5 / 64) % 256 # 茶色〜オレンジ | |
| rgb = hsv_to_rgb(rock_hue, 140, 100) | |
| LED.set(rgb, i) | |
| else | |
| # 砂地 | |
| sand_brightness = 70 + (sin_approx(time + x * 10 + y * 10) * 10) / 64 | |
| rgb = hsv_to_rgb(40, 40, sand_brightness) # 薄い黄色(砂) | |
| LED.set(rgb, i) | |
| end | |
| i += 1 | |
| end | |
| # 捕食者(影)が時々現れる | |
| if time % 30 < 10 | |
| predator_x = time % 5 | |
| predator_y = 0 | |
| predator_pos = predator_y * 5 + predator_x | |
| LED.set([0, 0, 8], predator_pos) # 暗い影 | |
| # タコが捕食者を感知して状態変化 | |
| # abs関数を使わずに距離を計算 | |
| dx = predator_x - octopus_x | |
| if dx < 0 | |
| dx = -dx | |
| end | |
| dy = predator_y - octopus_y | |
| if dy < 0 | |
| dy = -dy | |
| end | |
| if dx + dy < 3 | |
| # 三項演算子を使わずに条件分岐 | |
| if time % 20 < 10 | |
| octopus_state = 1 # 隠れる | |
| else | |
| octopus_state = 2 # 逃げる | |
| end | |
| end | |
| else | |
| octopus_state = 0 # 通常状態に戻る | |
| end | |
| # タコの動き | |
| case octopus_state | |
| when 0 # 通常:ゆっくり動く | |
| if time % 10 == 0 | |
| direction = time % 4 | |
| case direction | |
| when 0 then octopus_x = (octopus_x + 1) % 5 | |
| when 1 then octopus_y = (octopus_y + 1) % 5 | |
| when 2 then octopus_x = (octopus_x - 1 + 5) % 5 | |
| when 3 then octopus_y = (octopus_y - 1 + 5) % 5 | |
| end | |
| end | |
| when 1 # 隠れる:動かない | |
| # 何もしない | |
| when 2 # 逃げる:素早く移動 | |
| if time % 5 == 0 | |
| # 捕食者から離れる方向へ | |
| octopus_x = (octopus_x + 1) % 5 | |
| octopus_y = (octopus_y + 1) % 5 | |
| end | |
| end | |
| # タコの描画 | |
| octopus_pos = octopus_y * 5 + octopus_x | |
| # 状態に応じた色変化 | |
| octopus_hue = 0 | |
| saturation = 0 | |
| brightness = 0 | |
| case octopus_state | |
| when 0 # 通常:赤褐色 | |
| octopus_hue = (0 + sin_approx(color_phase) * 10 / 64) % 256 | |
| saturation = 200 | |
| brightness = 150 | |
| when 1 # 隠れる:周囲の環境に合わせる(砂色) | |
| octopus_hue = 40 | |
| saturation = 60 | |
| brightness = 80 | |
| when 2 # 逃げる:警戒色(明るい赤) | |
| octopus_hue = 0 | |
| saturation = 255 | |
| brightness = 255 | |
| end | |
| # タコの体 | |
| rgb = hsv_to_rgb(octopus_hue, saturation, brightness) | |
| LED.set(rgb, octopus_pos) | |
| # タコの触手(8本)- 各種イテレータを使わずにシンプルに記述 | |
| tentacle_dx = [-1, 1, 0, 0, -1, 1, -1, 1] | |
| tentacle_dy = [0, 0, -1, 1, -1, -1, 1, 1] | |
| i = 0 | |
| while i < 8 # 8本の触手 | |
| dx = tentacle_dx[i] | |
| dy = tentacle_dy[i] | |
| nx = (octopus_x + dx + 5) % 5 | |
| ny = (octopus_y + dy + 5) % 5 | |
| # 一部の触手だけを表示(すべてを表示すると密集しすぎる) | |
| if i < 4 || time % 10 < 5 | |
| tentacle_pos = ny * 5 + nx | |
| tentacle_hue = (octopus_hue + i * 5) % 256 # 触手ごとに少し色を変える | |
| tentacle_rgb = hsv_to_rgb(tentacle_hue, saturation, brightness / 2) | |
| LED.set(tentacle_rgb, tentacle_pos) | |
| end | |
| i += 1 | |
| end | |
| color_phase = (color_phase + 3) % 256 | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 4. 深海の発光生物 - 暗闇で光る深海魚 | |
| def deep_sea_bioluminescence | |
| creatures = [] | |
| i = 0 | |
| while i < 4 | |
| # [x, y, 色相, 明るさ, 明滅周期] | |
| creatures[i] = [i, i, 180 + i * 15, 150 + i * 20, 50 + i * 10] | |
| i += 1 | |
| end | |
| time = 0 | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 深海の暗い背景 | |
| i = 0 | |
| while i < 25 | |
| LED.set([0, 0, 2], i) # ほぼ真っ暗 | |
| i += 1 | |
| end | |
| # 時々、水中を漂う微粒子が光を反射 | |
| if time % 3 == 0 | |
| particle_pos = (pseudo_random(time) % 25) | |
| particle_brightness = 20 + (pseudo_random(time + 1) % 20) | |
| rgb = hsv_to_rgb(200, 50, particle_brightness) | |
| LED.set(rgb, particle_pos) | |
| end | |
| # 発光生物の更新と描画 | |
| idx = 0 | |
| while idx < creatures.size | |
| c = creatures[idx] | |
| x = c[0] | |
| y = c[1] | |
| hue = c[2] | |
| brightness = c[3] | |
| cycle = c[4] | |
| # ゆっくりと移動 | |
| if time % 15 == idx | |
| move_direction = pseudo_random(time + idx) % 4 | |
| case move_direction | |
| when 0 then x = (x + 1) % 5 | |
| when 1 then y = (y + 1) % 5 | |
| when 2 then x = (x - 1 + 5) % 5 | |
| when 3 then y = (y - 1 + 5) % 5 | |
| end | |
| end | |
| # 明滅パターン | |
| glow = (sin_approx(time * 256 / cycle) * 128) / 64 + 128 | |
| current_brightness = (brightness * glow) / 256 | |
| # 生物の描画 | |
| pos = y * 5 + x | |
| rgb = hsv_to_rgb(hue, 255, current_brightness) | |
| LED.set(rgb, pos) | |
| # 特別な発光パターン(アンコウのような誘導発光器) | |
| if idx == 0 && time % 20 < 10 | |
| lure_x = (x + 1) % 5 | |
| lure_y = (y - 1 + 5) % 5 | |
| lure_pos = lure_y * 5 + lure_x | |
| lure_brightness = 200 + (sin_approx(time * 10) * 55) / 64 | |
| rgb = hsv_to_rgb((hue + 30) % 256, 255, lure_brightness) | |
| LED.set(rgb, lure_pos) | |
| end | |
| # 生物の状態を更新 | |
| creatures[idx] = [x, y, hue, brightness, cycle] | |
| idx += 1 | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # 5. サンゴ礁 - 色とりどりのサンゴと熱帯魚 | |
| def coral_reef | |
| # サンゴの配置 | |
| coral_positions = [0, 1, 5, 6, 10, 15, 20, 21, 22] | |
| fish = [] | |
| # 初期の魚を生成 | |
| i = 0 | |
| while i < 3 | |
| # [x, y, 色相, 方向] | |
| fish[i] = [4 - i, i, i * 85, i % 4] | |
| i += 1 | |
| end | |
| time = 0 | |
| # 約10秒間のアニメーション | |
| count = 0 | |
| while count < 100 | |
| # 浅い海の背景(明るい青) | |
| i = 0 | |
| while i < 25 | |
| x = i % 5 | |
| y = i / 5 | |
| # サンゴをinclude?を使わないで判定 | |
| is_coral = false | |
| coral_idx = 0 | |
| while coral_idx < coral_positions.size | |
| if coral_positions[coral_idx] == i | |
| is_coral = true | |
| break | |
| end | |
| coral_idx += 1 | |
| end | |
| if is_coral | |
| # サンゴの描画 | |
| coral_type = i % 3 | |
| coral_hue = 0 | |
| coral_sat = 0 | |
| case coral_type | |
| when 0 # ピンク/紫のサンゴ | |
| coral_hue = (300 + sin_approx(time + i * 10) * 20 / 64) % 256 | |
| coral_sat = 200 | |
| when 1 # オレンジ/黄色のサンゴ | |
| coral_hue = (30 + sin_approx(time + i * 10) * 15 / 64) % 256 | |
| coral_sat = 220 | |
| when 2 # 緑/青緑のサンゴ | |
| coral_hue = (150 + sin_approx(time + i * 10) * 30 / 64) % 256 | |
| coral_sat = 180 | |
| end | |
| brightness = 150 + (sin_approx(time * 3 + i * 20) * 50) / 64 | |
| rgb = hsv_to_rgb(coral_hue, coral_sat, brightness) | |
| LED.set(rgb, i) | |
| else | |
| # 海水 | |
| water_brightness = 100 - (y * 10) # 深さによる明るさの変化 | |
| water_brightness += (sin_approx(time * 3 + x * 20 + y * 20) * 20) / 64 # 光の揺らぎ | |
| rgb = hsv_to_rgb(190, 150, water_brightness) | |
| LED.set(rgb, i) | |
| end | |
| i += 1 | |
| end | |
| # 魚の更新と描画 | |
| idx = 0 | |
| while idx < fish.size | |
| f = fish[idx] | |
| x = f[0] | |
| y = f[1] | |
| hue = f[2] | |
| direction = f[3] | |
| # 魚の動き | |
| if time % (idx + 3) == 0 | |
| case direction | |
| when 0 then x = (x + 1) % 5 | |
| when 1 then y = (y + 1) % 5 | |
| when 2 then x = (x - 1 + 5) % 5 | |
| when 3 then y = (y - 1 + 5) % 5 | |
| end | |
| # サンゴに当たったら方向転換 - include?を使わずに判定 | |
| fish_pos = y * 5 + x | |
| hit_coral = false | |
| coral_idx = 0 | |
| while coral_idx < coral_positions.size | |
| if coral_positions[coral_idx] == fish_pos | |
| hit_coral = true | |
| break | |
| end | |
| coral_idx += 1 | |
| end | |
| if hit_coral | |
| direction = (direction + 2) % 4 # 反対方向へ | |
| case direction | |
| when 0 then x = (x + 1) % 5 | |
| when 1 then y = (y + 1) % 5 | |
| when 2 then x = (x - 1 + 5) % 5 | |
| when 3 then y = (y - 1 + 5) % 5 | |
| end | |
| end | |
| # ランダムな方向転換 | |
| if pseudo_random(time + idx) < 30 # 約12%の確率 | |
| direction = (direction + 1) % 4 | |
| end | |
| end | |
| # 魚の描画 | |
| pos = y * 5 + x | |
| # 熱帯魚の鮮やかな色 | |
| fish_hue = (hue + time) % 256 | |
| fish_brightness = 200 + (sin_approx(time * 10 + idx * 50) * 55) / 64 | |
| rgb = hsv_to_rgb(fish_hue, 255, fish_brightness) | |
| LED.set(rgb, pos) | |
| # 魚の状態を更新 | |
| fish[idx] = [x, y, hue, direction] | |
| idx += 1 | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| count += 1 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| puts "熱帯魚の群れデモを開始" | |
| tropical_fish_school | |
| sleep 0.5 | |
| puts "クラゲの脈動デモを開始" | |
| jellyfish_pulsation | |
| sleep 0.5 | |
| puts "タコの擬態デモを開始" | |
| octopus_camouflage | |
| sleep 0.5 | |
| puts "深海の発光生物デモを開始" | |
| deep_sea_bioluminescence | |
| sleep 0.5 | |
| puts "サンゴ礁デモを開始" | |
| coral_reef | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラー表示(赤い波紋) | |
| error_count = 0 | |
| while true do | |
| y = 0 | |
| while y < 5 | |
| x = 0 | |
| while x < 5 | |
| pos = y * 5 + x | |
| # absを使わずに距離計算 | |
| dx = x - 2 | |
| if dx < 0 | |
| dx = -dx | |
| end | |
| dy = y - 2 | |
| if dy < 0 | |
| dy = -dy | |
| end | |
| distance = dx + dy | |
| if distance < 3 | |
| intensity = 64 - distance * 20 | |
| LED.set([intensity, 0, 0], pos) | |
| else | |
| LED.set([0, 0, 0], pos) | |
| end | |
| x += 1 | |
| end | |
| y += 1 | |
| end | |
| sleep 0.5 | |
| i = 0 | |
| while i < 25 | |
| LED.set([0, 0, 0], i) | |
| i += 1 | |
| end | |
| sleep 0.5 | |
| error_count += 1 | |
| break if Blink.req_reload? || error_count > 5 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix Gentle LED Demo | |
| # 目に優しい、ゆっくりとした光のパターン | |
| # mruby/c 3.3の制限事項に準拠 | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # やさしい呼吸パターン | |
| def gentle_breathing | |
| hue = 160 # 緑がかった青色(リラックス効果) | |
| phase = 0 | |
| 60.times do | |
| brightness = (sin_approx(phase) * 128) / 64 + 64 # 明るさを抑えめに | |
| rgb = hsv_to_rgb(hue, 128, brightness) # 彩度を抑えめに | |
| 25.times do |i| | |
| LED.set(rgb, i) | |
| end | |
| phase = (phase + 1) % 256 | |
| sleep 0.1 # ゆっくりとした変化 | |
| end | |
| end | |
| # 穏やかな波紋 | |
| def gentle_ripple | |
| center_x = 2 | |
| center_y = 2 | |
| phase = 0 | |
| 50.times do | |
| 5.times do |y| | |
| 5.times do |x| | |
| distance = (x - center_x).abs + (y - center_y).abs | |
| wave = (sin_approx((distance * 32 + phase) % 256) * 96) / 64 + 32 | |
| hue = 200 # 落ち着いた青色 | |
| rgb = hsv_to_rgb(hue, 128, wave) | |
| LED.set(rgb, y * 5 + x) | |
| end | |
| end | |
| phase = (phase + 2) % 256 # ゆっくりとした波紋 | |
| sleep 0.1 | |
| end | |
| end | |
| # 星空のきらめき | |
| def starry_night | |
| stars = [] | |
| phase = 0 # phaseを関数内で定義 | |
| 3.times do # 星の数を制限 | |
| x = (phase * 17 + 23) % 5 | |
| y = (phase * 13 + 7) % 5 | |
| stars << [x, y, 0] # [x, y, 明るさ] | |
| phase += 1 # 各星の位置を少しずつずらす | |
| end | |
| 40.times do |time| | |
| # 背景を暗い青に | |
| 25.times do |i| | |
| LED.set([0, 0, 1], i) | |
| end | |
| # 星のきらめき | |
| stars.each do |star| | |
| x, y, brightness = star | |
| pos = y * 5 + x | |
| new_brightness = (sin_approx((brightness + time * 8) % 256) * 32) / 64 + 16 | |
| rgb = hsv_to_rgb(40, 32, new_brightness) # 温かみのある光 | |
| LED.set(rgb, pos) | |
| end | |
| sleep 0.15 | |
| end | |
| end | |
| # 夕暮れグラデーション | |
| def sunset_gradient | |
| base_hue = 20 # オレンジ色 | |
| phase = 0 | |
| 40.times do | |
| 5.times do |y| | |
| hue = (base_hue + y * 4 + phase / 2) % 256 | |
| rgb = hsv_to_rgb(hue, 192, 96) # 明るさを抑えめに | |
| 5.times do |x| | |
| LED.set(rgb, y * 5 + x) | |
| end | |
| end | |
| phase = (phase + 1) % 256 | |
| sleep 0.2 # ゆっくりとした変化 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| gentle_breathing | |
| sleep 0.5 | |
| gentle_ripple | |
| sleep 0.5 | |
| starry_night | |
| sleep 0.5 | |
| sunset_gradient | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラー時は非常に暗い赤色でゆっくり点滅 | |
| while true do | |
| 25.times { |i| LED.set([8, 0, 0], i) } | |
| sleep 2 | |
| 25.times { |i| LED.set([0, 0, 0], i) } | |
| sleep 2 | |
| break if Blink.req_reload? | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix Vibe Coding Demo | |
| # 心震える感覚を与えるリズミカルなLEDパターン | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # ビースケープ - バイブスがうねるベースのようなパターン | |
| def bass_vibe | |
| base_hue = 280 # 紫外線のようなカラー | |
| phase = 0 | |
| intensity = 0 | |
| direction = 1 | |
| 40.times do | |
| # ベースのリズムを生成 | |
| beat = sin_approx(phase * 12) * 192 / 64 | |
| 5.times do |y| | |
| 5.times do |x| | |
| # うねるようなベースパターン | |
| wave = sin_approx((x * 32 + y * 24 + phase * 4) % 256) * 128 / 64 | |
| # リズミカルな輝度変化 | |
| brightness = 96 + (beat * wave) / 192 | |
| # バイブ感のある色変化 | |
| hue = (base_hue + wave / 2 + beat) % 256 | |
| saturation = 192 + beat / 4 | |
| rgb = hsv_to_rgb(hue, saturation, brightness) | |
| LED.set(rgb, y * 5 + x) | |
| end | |
| end | |
| phase = (phase + 1) % 256 | |
| intensity += direction | |
| if intensity >= 255 || intensity <= 0 | |
| direction = -direction | |
| end | |
| # リズミカルな待機時間 | |
| if phase % 8 == 0 | |
| sleep 0.08 # ビートの強拍 | |
| else | |
| sleep 0.04 # ビートの弱拍 | |
| end | |
| end | |
| end | |
| # ネオンドリップ - 現代的でトレンディなネオンの雫 | |
| def neon_drip | |
| drops = [] | |
| 5.times do |i| | |
| drops << [i, -1, (i * 73) % 256] # [x, y, hue] | |
| end | |
| pseudo_random = 123 | |
| 50.times do |time| | |
| # 背景を暗く | |
| 25.times do |i| | |
| LED.set([1, 1, 2], i) | |
| end | |
| # 雫の更新と描画 | |
| drops.each_with_index do |drop, i| | |
| x, y, hue = drop | |
| # 雫の位置を更新 | |
| if time % 3 == 0 # 3フレームごとに下に移動 | |
| y += 1 | |
| end | |
| # 画面外に出たら新しい雫を作成 | |
| if y >= 5 | |
| pseudo_random = (pseudo_random * 17 + 23) % 256 | |
| y = -1 | |
| hue = (hue + pseudo_random) % 256 | |
| end | |
| drops[i] = [x, y, hue] | |
| # 雫を描画 | |
| if y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| brightness = 128 + (sin_approx(time * 10) * 64) / 64 | |
| rgb = hsv_to_rgb(hue, 255, brightness) | |
| LED.set(rgb, pos) | |
| # 雫の尾を描画 | |
| tail_y = y - 1 | |
| if tail_y >= 0 | |
| tail_pos = tail_y * 5 + x | |
| rgb = hsv_to_rgb(hue, 200, brightness / 2) | |
| LED.set(rgb, tail_pos) | |
| end | |
| end | |
| end | |
| # リズミカルな待機 | |
| if time % 10 == 0 | |
| sleep 0.12 # 小休止 | |
| else | |
| sleep 0.06 | |
| end | |
| end | |
| end | |
| # ロー・ファイ・グリッチ - 意図的なグリッチ効果 | |
| def lofi_glitch | |
| hue = 180 # シアンをベースに | |
| phase = 0 | |
| pseudo_random = 42 | |
| 60.times do |time| | |
| # グリッチの強度を変化 | |
| glitch_intensity = sin_approx(time * 5) * 64 / 64 | |
| # ランダムグリッチを生成 | |
| pseudo_random = (pseudo_random * 17 + 23) % 256 | |
| # グリッチパターンを生成 | |
| is_glitch_frame = pseudo_random < 64 # 約25%の確率でグリッチフレーム | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 基本的な波模様 | |
| wave = sin_approx((x * 24 + y * 32 + time * 3) % 256) | |
| current_hue = (hue + wave / 2) % 256 | |
| if is_glitch_frame | |
| # グリッチ効果 | |
| if ((x + y + pseudo_random) % 7) < 3 | |
| # ノイズの多いピクセル | |
| pixel_random = (pseudo_random * (x+1) * (y+1)) % 256 | |
| current_hue = (current_hue + 128) % 256 # 補色 | |
| rgb = hsv_to_rgb(current_hue, 128, 192 + (pixel_random % 64)) | |
| else | |
| # 暗いピクセル | |
| rgb = hsv_to_rgb(current_hue, 192, 48) | |
| end | |
| else | |
| # 通常のレトロ波紋 | |
| brightness = 128 + wave / 2 | |
| rgb = hsv_to_rgb(current_hue, 192, brightness) | |
| end | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| # グリッチフレームのときは短く表示 | |
| if is_glitch_frame | |
| sleep 0.02 | |
| else | |
| # 通常のリズミカルな待機 | |
| if time % 8 == 0 | |
| sleep 0.1 | |
| else | |
| sleep 0.05 | |
| end | |
| end | |
| phase = (phase + 1) % 256 | |
| end | |
| end | |
| # ハイパーウェーブ - 未来的な波動効果 | |
| def hyper_wave | |
| time = 0 | |
| energy = 0 | |
| direction = 1 | |
| 45.times do | |
| # エネルギー波形を生成 | |
| wave_power = sin_approx(time * 5) * 128 / 64 + 128 | |
| 5.times do |y| | |
| 5.times do |x| | |
| # 波の中心からの距離 | |
| dx = x - 2 | |
| dy = y - 2 | |
| distance = dx.abs + dy.abs # マンハッタン距離 | |
| # 波動効果 | |
| wave = sin_approx((distance * 64 + time * 8) % 256) | |
| wave_hue = (210 + wave * 30 / 64 + time) % 256 # 青から紫へ | |
| # エネルギーレベルによる輝度 | |
| brightness = 128 + (wave * energy) / 255 | |
| # エネルギー波動を表現 | |
| saturation = 128 + wave_power / 4 | |
| rgb = hsv_to_rgb(wave_hue, saturation, brightness) | |
| LED.set(rgb, y * 5 + x) | |
| end | |
| end | |
| # エネルギーの増減 | |
| energy += direction * 4 | |
| if energy >= 255 || energy <= 0 | |
| direction = -direction | |
| end | |
| # リズミカルな待機 | |
| beat_interval = wave_power / 128 # 0.5-1.5の範囲 | |
| sleep 0.03 + (beat_interval * 0.03) | |
| time += 1 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| bass_vibe # バイブスがうねるベース | |
| sleep 0.3 | |
| neon_drip # 現代的でトレンディなネオンの雫 | |
| sleep 0.3 | |
| lofi_glitch # レトロフューチャーなグリッチ効果 | |
| sleep 0.3 | |
| hyper_wave # 未来的な波動効果 | |
| sleep 0.3 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラー時はサイバーパンク的な赤と青の点滅 | |
| phase = 0 | |
| while true do | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| if (x + y + phase) % 2 == 0 | |
| LED.set([32, 0, 0], pos) # 赤 | |
| else | |
| LED.set([0, 0, 32], pos) # 青 | |
| end | |
| end | |
| end | |
| phase += 1 | |
| sleep 0.2 | |
| break if Blink.req_reload? | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # M5 Atom Matrix 天気・気象アニメーション | |
| # 直感的に理解できる天気現象を5x5 LEDマトリックスで表現 | |
| # mruby/c 3.3の制限事項と注意点 | |
| # 使用可能な機能: | |
| # - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ | |
| # - 配列操作(each, each_with_index) | |
| # - 条件分岐(if, case) | |
| # - ループ(times, while, downto) | |
| # - LED.set([r,g,b], pos) - LEDの色設定 | |
| # - sleep(秒数) - 待機 | |
| # - Blink.req_reload? - リロードチェック | |
| # | |
| # 使用できない機能: | |
| # - Math モジュール(sin, cos など) | |
| # - rand - 乱数生成 | |
| # - LED.get - 現在のLED色の取得 | |
| # - respond_to? - メソッド存在チェック | |
| # - reverse, reverse_each - 配列の逆順処理 | |
| # - 浮動小数点数の%演算 | |
| # - backtrace - エラーのスタックトレース | |
| # HSVからRGBへの変換(整数演算版) | |
| def hsv_to_rgb(h, s, v) | |
| h = h - (h / 256) * 256 | |
| s = s > 255 ? 255 : (s < 0 ? 0 : s) | |
| v = v > 255 ? 255 : (v < 0 ? 0 : v) | |
| c = (v * s) / 255 | |
| h_section = (h * 6) / 256 | |
| h_mod = ((h * 6 * 255) / 256) - (h_section * 255) | |
| x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255 | |
| m = v - c | |
| r, g, b = case h_section | |
| when 0 then [c, x, 0] | |
| when 1 then [x, c, 0] | |
| when 2 then [0, c, x] | |
| when 3 then [0, x, c] | |
| when 4 then [x, 0, c] | |
| when 5 then [c, 0, x] | |
| else [0, 0, 0] | |
| end | |
| [(r + m) / 4, (g + m) / 4, (b + m) / 4] | |
| end | |
| # 簡易サイン波生成(整数演算版) | |
| def sin_approx(x) | |
| x = x - (x / 256) * 256 # 0-255の範囲に正規化 | |
| if x < 64 | |
| x | |
| elsif x < 128 | |
| 128 - x | |
| elsif x < 192 | |
| x - 192 | |
| else | |
| 256 - x | |
| end | |
| end | |
| # 疑似乱数生成(線形合同法) | |
| def pseudo_random(seed) | |
| (seed * 17 + 23) % 256 | |
| end | |
| # 1. 晴れ - 太陽と青空のアニメーション | |
| def sunny_animation | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 空の青色 | |
| 25.times do |i| | |
| # 青空(上部ほど明るい青) | |
| y = i / 5 | |
| sky_brightness = 180 - y * 15 | |
| rgb = hsv_to_rgb(210, 180, sky_brightness) | |
| LED.set(rgb, i) | |
| end | |
| # 太陽の位置と大きさ | |
| sun_x = 2 | |
| sun_y = 1 | |
| # 太陽の光線の強さ(サイン波で変動) | |
| sun_power = 128 + (sin_approx(time * 5) * 64) / 64 | |
| # 太陽と光線を描画 | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| # 太陽からの距離 | |
| distance = (x - sun_x).abs + (y - sun_y).abs | |
| if distance <= 1 | |
| # 太陽本体(黄色) | |
| brightness = 200 + (sin_approx(time * 10) * 55) / 64 | |
| rgb = hsv_to_rgb(40, 230, brightness) | |
| LED.set(rgb, pos) | |
| elsif distance <= 3 | |
| # 光線(距離に応じて弱くなる) | |
| ray_strength = sun_power - distance * 40 | |
| if ray_strength > 0 | |
| # 太陽光線(やや橙色) | |
| rgb = hsv_to_rgb(35, 200, ray_strength) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # 2. 曇り - 動く雲のアニメーション | |
| def cloudy_animation | |
| cloud_positions = [] | |
| # 複数の雲を作成 | |
| 3.times do |i| | |
| # [x, 幅, 高さ] | |
| cloud_positions << [i * 3 - 2, 3, 2] | |
| end | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 背景(やや曇った空) | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(210, 40, 140) # 薄い青灰色 | |
| LED.set(rgb, i) | |
| end | |
| # 雲を動かして描画 | |
| cloud_positions.each_with_index do |cloud, i| | |
| x_pos, width, height = cloud | |
| # 雲を横に移動 | |
| x_pos = (x_pos + 0.2).to_f | |
| if x_pos > 7 | |
| x_pos = -4 # 画面外から再登場 | |
| end | |
| cloud_positions[i][0] = x_pos | |
| # 雲を描画 | |
| cloud_y = i % 2 # 雲の高さをずらす | |
| width.times do |w| | |
| height.times do |h| | |
| x = (x_pos + w).to_i | |
| y = cloud_y + h | |
| if x >= 0 && x < 5 && y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| # 雲の明るさを微妙に変化(立体感) | |
| cloud_brightness = 200 + (sin_approx(time * 3 + i * 50) * 30) / 64 | |
| # 雲の色(白〜薄いグレー) | |
| rgb = hsv_to_rgb(0, 0, cloud_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # 3. 雨 - 雨粒が落ちるアニメーション | |
| def rainy_animation | |
| drops = [] | |
| 6.times do |i| | |
| # [x, y, 速度, 強度] | |
| drops << [pseudo_random(i * 10) % 5, pseudo_random(i * 20) % 5, 1 + pseudo_random(i) % 2, 140 + pseudo_random(i + 50) % 60] | |
| end | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 背景(雨の空) | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(210, 60, 80) # 暗い青灰色 | |
| LED.set(rgb, i) | |
| end | |
| # 雨粒を更新して描画 | |
| drops.each_with_index do |drop, i| | |
| x, y, speed, intensity = drop | |
| # 古い位置の雨粒を消す(背景色で上書き) | |
| if y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| rgb = hsv_to_rgb(210, 60, 80) # 背景色 | |
| LED.set(rgb, pos) | |
| end | |
| # 雨粒を下に移動 | |
| y = (y + speed) % (5 + 3) # 画面外も含めてループ | |
| # 画面外から再登場するとき、x位置をランダムに変更 | |
| if y < 0 | |
| x = pseudo_random(time + i * 10) % 5 | |
| end | |
| # 雨粒の状態を更新 | |
| drops[i] = [x, y, speed, intensity] | |
| # 雨粒を描画(画面内の場合のみ) | |
| if y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| # 雨粒の色(青) | |
| rain_brightness = intensity - (pseudo_random(time + i) % 30) | |
| rgb = hsv_to_rgb(200, 180, rain_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # 4. 雪 - 雪の結晶が舞い落ちるアニメーション | |
| def snowy_animation | |
| snowflakes = [] | |
| 8.times do |i| | |
| # [x, y, 落下速度, 揺れ幅, 揺れ位相] | |
| snowflakes << [ | |
| pseudo_random(i * 11) % 5, | |
| pseudo_random(i * 7) % 8 - 3, # 一部は画面外からスタート | |
| pseudo_random(i * 13) % 4 + 1, # 1-4の速度(整数に変更) | |
| pseudo_random(i * 19) % 3, # 0-2の揺れ幅 | |
| pseudo_random(i * 23) # 揺れの初期位相 | |
| ] | |
| end | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 背景(冬の空) | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(210, 20, 100) # 薄い青色(冬の空) | |
| LED.set(rgb, i) | |
| end | |
| # 雪を更新して描画 | |
| snowflakes.each_with_index do |flake, i| | |
| x, y, speed, sway_width, phase = flake | |
| # 雪の動き(下に落ちながら左右に揺れる) | |
| # 速度を整数値として扱う(2フレームに1回動くなど) | |
| if time % 2 == 0 | |
| new_y = y + (speed / 2) # 速度を整数で調整 | |
| else | |
| new_y = y | |
| end | |
| # 左右の揺れ(整数演算に変更) | |
| phase_new = (phase + 7) % 256 | |
| sway = (sin_approx(phase_new) * sway_width) / 64 # 小数点を削除 | |
| new_x = (x + sway + 5) % 5 # 整数値になるので問題なし | |
| # 画面外に出たら上から再登場 | |
| if new_y >= 5 | |
| new_y = -1 | |
| new_x = pseudo_random(time + i * 31) % 5 | |
| end | |
| # 雪の状態を更新 | |
| snowflakes[i] = [new_x, new_y, speed, sway_width, phase_new] | |
| # 雪を描画(画面内の場合のみ) | |
| if new_y >= 0 && new_y < 5 | |
| pos = new_y * 5 + new_x | |
| # 雪の色(純白) | |
| snow_brightness = 220 + (sin_approx(time * 10 + i * 20) * 35) / 64 # キラキラ効果 | |
| rgb = hsv_to_rgb(180, 5, snow_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # 5. 雷雨 - 雨に雷が加わるアニメーション | |
| def thunderstorm_animation | |
| # 雨粒の初期化 | |
| raindrops = [] | |
| 7.times do |i| | |
| # [x, y, 速度] | |
| raindrops << [pseudo_random(i * 10) % 5, pseudo_random(i * 20) % 5, 1 + pseudo_random(i) % 2] | |
| end | |
| # 雷の状態(0=なし、1-5=フラッシュ強度) | |
| lightning = 0 | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 通常の雨の背景(暗めの空) | |
| background_brightness = 60 | |
| # 雷が光っているとき | |
| if lightning > 0 | |
| # 背景を明るく(雷の光) | |
| background_brightness = 60 + lightning * 30 | |
| lightning -= 1 # 雷の強度を徐々に弱める | |
| end | |
| # 背景を描画 | |
| 25.times do |i| | |
| rgb = hsv_to_rgb(210, 60, background_brightness) | |
| LED.set(rgb, i) | |
| end | |
| # 雨粒を更新して描画 | |
| raindrops.each_with_index do |drop, i| | |
| x, y, speed = drop | |
| # 雨粒を下に移動 | |
| y = (y + speed) % (5 + 3) # 画面外も含めてループ | |
| # 画面外から再登場するとき、x位置をランダムに変更 | |
| if y < 0 | |
| x = pseudo_random(time + i * 10) % 5 | |
| end | |
| # 雨粒の状態を更新 | |
| raindrops[i] = [x, y, speed] | |
| # 雨粒を描画(画面内の場合のみ) | |
| if y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| # 雨粒の色(青)- 雷の光を反映 | |
| rain_brightness = 140 + lightning * 20 | |
| rgb = hsv_to_rgb(200, 180, rain_brightness) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| # 時々雷を発生 | |
| if lightning == 0 && pseudo_random(time * 13) < 20 # 約8%の確率 | |
| lightning = 5 # 最大強度の雷 | |
| # 雷のパターンを描画 | |
| lightning_path = [] | |
| # 雷の経路を生成(上から下へジグザグ) | |
| x = 2 # 中央からスタート | |
| 5.times do |y| | |
| pos = y * 5 + x | |
| lightning_path << pos | |
| # 次のxを決定(左右にジグザグ) | |
| x_change = pseudo_random(time + y * 10) % 3 - 1 # -1, 0, 1 | |
| x = (x + x_change + 5) % 5 | |
| end | |
| # 雷を描画 | |
| lightning_path.each do |pos| | |
| rgb = hsv_to_rgb(60, 100, 240) # 明るい黄色 | |
| LED.set(rgb, pos) | |
| end | |
| # 雷の音/衝撃を表現するための短い休止 | |
| sleep 0.2 | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # 6. 虹 - 雨上がりの虹のアニメーション | |
| def rainbow_animation | |
| time = 0 | |
| # 約8秒間のアニメーション | |
| 80.times do | |
| # 背景(雨上がりの空) | |
| 25.times do |i| | |
| x = i % 5 | |
| y = i / 5 | |
| if y < 2 | |
| # 上部は青空 | |
| sky_brightness = 180 - y * 15 | |
| rgb = hsv_to_rgb(210, 100, sky_brightness) | |
| else | |
| # 下部は地面(緑) | |
| ground_brightness = 120 - (y - 2) * 20 | |
| rgb = hsv_to_rgb(120, 160, ground_brightness) | |
| end | |
| LED.set(rgb, i) | |
| end | |
| # 時間の経過とともに虹が現れる | |
| if time > 10 | |
| # 虹の位置(中央のやや上) | |
| rainbow_y = 2 | |
| # 虹の弧 | |
| rainbow_arc = [ | |
| [0, 4], # 左端 | |
| [1, 3], # 左から2番目 | |
| [2, 2], # 中央 | |
| [3, 3], # 右から2番目 | |
| [4, 4] # 右端 | |
| ] | |
| rainbow_arc.each_with_index do |coords, i| | |
| x, y_offset = coords | |
| y = rainbow_y - y_offset | |
| if y >= 0 && y < 5 | |
| pos = y * 5 + x | |
| # 虹の色(赤から紫へ) | |
| # 色相を0(赤)から300(紫)の範囲で変化 | |
| hue = (i * 60) % 256 | |
| # 徐々に現れる効果 | |
| fade_in = time - 10 | |
| if fade_in > 20 | |
| fade_in = 20 | |
| end | |
| brightness = 100 + fade_in * 7 | |
| # 虹をきらめかせる | |
| shimmer = (sin_approx(time * 10 + i * 50) * 20) / 64 | |
| rgb = hsv_to_rgb(hue, 230, brightness + shimmer) | |
| LED.set(rgb, pos) | |
| end | |
| end | |
| end | |
| time += 1 | |
| sleep 0.1 | |
| end | |
| end | |
| # メインループ | |
| begin | |
| while true do | |
| puts "晴れのアニメーションを開始" | |
| sunny_animation | |
| sleep 0.5 | |
| puts "曇りのアニメーションを開始" | |
| cloudy_animation | |
| sleep 0.5 | |
| puts "雨のアニメーションを開始" | |
| rainy_animation | |
| sleep 0.5 | |
| puts "雪のアニメーションを開始" | |
| snowy_animation | |
| sleep 0.5 | |
| puts "雷雨のアニメーションを開始" | |
| thunderstorm_animation | |
| sleep 0.5 | |
| puts "虹のアニメーションを開始" | |
| rainbow_animation | |
| sleep 0.5 | |
| break if Blink.req_reload? | |
| end | |
| rescue => e | |
| puts "エラーが発生しました:" | |
| puts " クラス: #{e.class}" | |
| puts " メッセージ: #{e.message}" | |
| # エラー表示(天気マーク的な点滅) | |
| error_count = 0 | |
| while true do | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| if (x == 2 && y == 2) || (x - 2).abs + (y - 2).abs == 1 | |
| LED.set([64, 0, 0], pos) # 警告色(赤) | |
| else | |
| LED.set([0, 0, 0], pos) # 消灯 | |
| end | |
| end | |
| end | |
| sleep 0.5 | |
| 5.times do |y| | |
| 5.times do |x| | |
| pos = y * 5 + x | |
| LED.set([0, 0, 0], pos) # 全消灯 | |
| end | |
| end | |
| sleep 0.5 | |
| error_count += 1 | |
| break if Blink.req_reload? || error_count > 5 | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment