← 都来码 / Dolaima← 例程索引ruby/test/catch.rb原文

ruby/test/catch.rb · 7653 字节 · 下载原文

# 接方块 —— 用 **Ruby** 写的手机游戏

# Catch the ball — a mobile game written in **Ruby**

#

# 玩法:左右方向键移动底部挡板,把落下来的球弹回去;没接住就结束。每接住一次 +10 分。

# Gameplay: left/right arrow keys move the paddle at the bottom, bouncing the falling ball back; miss it and the game ends. Each catch scores +10 points.

#

# ◆ 手机那套 UI

# ◆ The phone UI stack

#

# 开窗 / 绘图 / 输入 / 定时器是 C 写的(`Lib/shared/src/vmlui.c` → `vmlui.vml`),

# Window / drawing / input / timers are written in C (`Lib/shared/src/vmlui.c` → `vmlui.vml`),

# 由 `vmltool.config.xml` 的 `<Language Name="ruby" Libs="vmlui.vml">` 挂上来。

# pulled in by `vmltool.config.xml`'s `<Language Name="ruby" Libs="vmlui.vml">`.

#

# ◆ 为什么这份是**完全平铺**的(没有 def、逻辑内联在主循环里)

# ◆ Why this file is **completely flat** (no def, the logic inlined into the main loop)

#

# 两条本前端实测出来的限制(2026-09-17):

# Two limits measured in practice on this frontend (2026-09-17):

#   ① **词法器不认 `&&`** —— `Unexpected char: &`。多个条件只能写成嵌套 `if`。

#   ① **The lexer does not accept `&&`** — `Unexpected char: &`. Multiple conditions can only be written as nested `if`.

#   ② **`def` 的支持有问题** —— 一个最朴素的 `def resetGame … end` 会报

#   ② **`def` support is broken** — the plainest `def resetGame … end` reports

#      `Unexpected token: End(end)`(报在配对的 `end` 上,像是块没被正确打开)。

#      `Unexpected token: End(end)` (reported on the matching `end`, as if the block was never opened properly).

#      `corpus/ruby/skel.rb` 里**一个函数都没有**,所以这条从来没被骨架覆盖到。

#      `corpus/ruby/skel.rb` has **not a single function**, so this item was never covered by the skeleton.

#      ⇒ 本份例程不用任何方法,状态放顶层局部变量、draw/tick 内联进主循环。

#      ⇒ This example uses no methods at all: state lives in top-level locals, draw/tick are inlined into the main loop.

#   这两条要修的话在前端(`RubyCompiler`),不是例程的问题。

#   Fixing these two belongs in the frontend (`RubyCompiler`), not in the example.

#

# 另一处写法:颜色写负数十进制;裸调库函数不写声明(同 skel.rb)。

# Another convention: colors are written as negative decimals; library functions are called bare with no declaration (same as skel.rb).


# 状态:0=挡板x 1=球x 2=球y 3=球dx 4=球dy 5=分数 6=最高 7=存活 8=屏宽 9=屏高

# State: 0=paddle x 1=ball x 2=ball y 3=ball dx 4=ball dy 5=score 6=best 7=alive 8=screen width 9=screen height

# UI 库的显式引用(用户 2026-10-01 定的口径,见 docs/UI库导入约定.md)。

# Explicit reference to the UI library (rule set by the user on 2026-10-01; see the UI-library import convention doc under docs).

# 声明文件 Lib/ruby/vml_ui.rb 由 GenLib 生成、不要手改:

#     dotnet run --project tools/GenLib -- -u -c vmlui -l ruby -o Lib/ruby/vml_ui.rb

# ⚠ 实测:本条是**文档级**引用 —— 删掉它示例照样编得过、跑得对(Ruby 按名字解析)。

# ⚠ Measured: this line is a **documentation-level** reference — delete it and the example still compiles and runs (Ruby resolves by name).

require 'vml_ui'

a0 = 0
a1 = 0
a2 = 0
a3 = 0
a4 = 0
a5 = 0
a6 = 0
a7 = 0

lang = ui_scr_get_language()
# ⚠ **先开窗、再问尺寸**:开窗前 `ui_scr_get_w/h` 只能按**默认 chrome**(假设要手柄)估算,

#   与真正开出来的画布对不上(见 E141)。⇒ 开窗尺寸传 0 交给宿主,**开完再问**真实可用绘图区。

# ⚠ **Open the window first, then ask for the size**: before the window exists `ui_scr_get_w/h`

#   can only estimate from the **default chrome** (which assumes a gamepad) and will not match the

#   real canvas (see E141). So pass 0 here and ask for the real usable area **after** opening.

if lang == 0 then ui_win_open("接方块", 0, 0) else ui_win_open("Catch", 0, 0) end
w = ui_scr_get_w()
h = ui_scr_get_h()
if w <= 0
  w = 360
end
if h <= 0
  h = 620
end
ui_scr_set_keep_on(1)

a0 = w / 2 - 40
a1 = w / 2
a2 = 70
a3 = 3
a4 = 5
a5 = 0
a6 = 0
a7 = 1

tid = ui_timer_set(40, 0)

while ui_win_is_closed() == 0

  # ── draw ──

  ui_2d_clear(-15724520)
  if lang == 0 then ui_2d_text(8, 8, "得分", -6643536, 13, VML_ANCHOR_LEFT) else ui_2d_text(8, 8, "Score", -6643536, 13, VML_ANCHOR_LEFT) end
  ui_2d_rect(58, 11, a5, 10, -11409298, 1, 0, 0)
  if lang == 0 then ui_2d_text(w / 2, 8, "最高", -6643536, 13, VML_ANCHOR_CENTER) else ui_2d_text(w / 2, 8, "Best", -6643536, 13, VML_ANCHOR_CENTER) end
  ui_2d_rect(w / 2 + 46, 11, a6, 10, -63488, 1, 0, 0)
  ui_2d_rect(a0, h - 40, 80, 12, -63488, 1, 0, 6)
  ui_2d_circle(a1, a2, 9, -131246, 1, 0)
  if a7 == 0
    if lang == 0 then ui_2d_text(w / 2, h / 2, "按回车重开", -131246, 16, VML_ANCHOR_CENTER) else ui_2d_text(w / 2, h / 2, "Press Enter to restart", -131246, 16, VML_ANCHOR_CENTER) end
  end
  ui_2d_present()

  t = ui_msg_fetch_wait(0)
  if t == 10
    break
  end

  if t == 9
    if a7 != 0
      a1 = a1 + a3
      a2 = a2 + a4
      if a1 < 10
        a1 = 10
        a3 = 0 - a3
      end
      if a1 > w - 10
        a1 = w - 10
        a3 = 0 - a3
      end
      if a2 < 30
        a2 = 30
        a4 = 0 - a4
      end
      # ⚠ 不认 `&&` ⇒ 嵌套 if

      # ⚠ no `&&` ⇒ nested if

      if a2 > h - 52
        if a2 < h - 30
          if a1 > a0 - 9
            if a1 < a0 + 89
              a4 = 0 - a4
              a2 = h - 52
              a5 = a5 + 10
              if a5 > a6
                a6 = a5
              end
              # 音效:单音 ui_audio_beep(v0.96.509 从音序器换回来 ——

              # Sound: single-tone ui_audio_beep (v0.96.509 switched back from the sequencer —

              #   那一版多声部叠加 / 长音拖尾在真机上破音)

              #   that version's multi-voice layering / long note tails crackled on real devices)

              ui_audio_beep(1047, 165)
            end
          end
        end
      end
      if a2 > h
        a7 = 0
        # 音效:单音 ui_audio_beep;**结局音取最低音**(接住 1047 / 没接住 131,差得开)

        # Sound: single-tone ui_audio_beep; **the ending tone takes the lowest pitch** (caught 1047 / missed 131, far apart)

        ui_audio_beep(131, 320)
        if lang == 0 then r = ui_dlg_msg("接方块", "没接住,这一局结束。\n再来一局?(选「否」退出)", VML_DLG_INFO) else r = ui_dlg_msg("Catch", "Missed. Round over.\nPlay again? (choose 'No' to quit)", VML_DLG_INFO) end
        if r != 0
            ui_win_close()
            break
        end
        a0 = w / 2 - 40
        a1 = w / 2
        a2 = 70
        a3 = 3
        a4 = 5
        a5 = 0
        a7 = 1
      end
    end
  end

  if t == 1
    k = ui_msg_get_a()
    if k == 27
      break
    end
    if k == 37
      a0 = a0 - 20
      if a0 < 4
        a0 = 4
      end
    end
    if k == 39
      a0 = a0 + 20
      if a0 > w - 84
        a0 = w - 84
      end
    end
    if k == 13
      a0 = w / 2 - 40
      a1 = w / 2
      a2 = 70
      a3 = 3
      a4 = 5
      a5 = 0
      a7 = 1
    end
  end
end

ui_timer_kill(tid)
ui_scr_set_keep_on(0)
ui_win_close()