第 8 课 · Découverte

位置表

一格 = 一个地址,上面一个名字:世界装进一个 Hash——墙,就是被占的格子。

~11 min7 个关卡
▶ 视频🧩 练习📄 备忘单

42 秒 · 含中文字幕 · 8.7 Mo

你会学会的 7 件事

第 8 课:位置表

Regarde la leçon (42 s) : ton monde n'a pas de décor, il a une table. Qui est où — et qui ne peut pas passer.

一格 = 一个地址

Écris `case_de(x, y)` : la clé de la map pour la case (3, 4) est `"3,4"` — x, une virgule, y. Toute la case tient dans une chaîne.

安置精灵

Écris `pose(carte, x, y, nom)` qui renvoie une NOUVELLE map avec le sprite posé sur la case — l'original ne bouge jamais. Tu ranges, tu ne détruits.

格子被占了吗?

Écris `occupe?(carte, x, y)` : true si un sprite est sur cette case. On interroge l'ADRESSE — `key?` — pas la valeur : le vide n'a pas de nom.

谁遇到谁?

Écris `rencontre(carte, x, y)` : le nom du sprite sur la case — ou `nil` si elle est vide. Dans un vrai jeu, c'est ce retour qui déclenche tout : dégât, pièce, dialogue.

每秒更新六十次

Écris `tic(secondes)` : le nombre de fois où la map est relue et recalculée — 60 fois par seconde, pas une de plus. Une seconde de jeu, soixante calculs.

第 8 课:OK

Sans aide : `avance(carte, x, y, dx, dy)` — un pas vers `[x + dx, y + dy]`. Si cette case est déjà occupée (un mur, un autre sprite), le pas est IMPOSSIBLE : tu restes à `[x, y]`.

分享这一课

← 全部课程

📄 备忘单

打印出来贴显示器上,就当是你自己的主意

📄 备忘单 — 位置表(世界就是一个 Hash)

你的游戏没有世界,只有一张表。一个格子 = 一条地址,上面一个名字——而墙,就是被占的格子。

这张表的四个词

词 它是什么 例子
格子 一条地址 x,y "3,4"
表 所有格子,一个 Hash carte = {}
心跳 一次更新 每秒 60 次
墙 被占的格子 carte["5,5"] = "mur"

一个格子 = 一条地址

def case_de(x, y)
  "#{x},#{y}"     # x 在前,逗号,y 在后
end

case_de(3, 4)     # => "3,4"

整个格子装进一个字符串:这就是你 Hash 的键。4,3 是另一个格子。

安置精灵:只复制,绝不销毁

def pose(carte, x, y, nom)
  carte.merge("#{x},#{y}" => nom)   # 新 Hash,原件完好
end

pose({}, 1, 2, "joueur")   # => { "1,2" => "joueur" }

merge 复制,= 毁掉。从第 6 课起规则没变过——现在用在整个世界上。

占用还是空:key? 对值

carte = { "3,4" => "joueur" }

carte.key?("3,4")   # => true   地址【存在】
carte.key?("0,0")   # => false  没人住那
carte["0,0"]        # => nil    空没有名字

key? 告诉你格子被占了——不管值是什么。这就是游戏每秒问六十遍的问题。

谁遇到谁

def rencontre(carte, x, y)
  carte["#{x},#{y}"]     # 名字……或者 nil
end

rencontre(carte, 3, 4)   # => "joueur"
rencontre(carte, 0, 0)   # => nil

回来 nil:路上什么也没有。回来一个名字:伤害、金币、对话——全部玩法都从这个返回值开始。

每秒六十次更新

def tic(secondes)
  secondes * 60      # 一秒游戏 = 重读这张表六十遍
end

tic(1)               # => 60

每次心跳:重读表、移动、再比对。游戏不是在动——它在重算。

面对墙,这一步不可能

def avance(carte, x, y, dx, dy)
  cible = "#{x + dx},#{y + dy}"
  carte.key?(cible) ? [x, y] : [x + dx, y + dy]
end

avance({}, 2, 2, 1, 0)                   # => [3, 2]  格子空:我前进
avance({ "3,2" => "mur" }, 2, 2, 1, 0)   # => [2, 2]  格子被占:我留下

没有物理,没有引擎的「碰撞」:一条已被占用的地址、一个 key?、一个选择。

以后别再说

  • 「精灵撞墙了」 → 目标格已经在表里——你返回旧位置
  • 「我需要碰撞引擎」 → 你的 Hash 从第 3 课起就在干这活。

SlopCourse Microduck — 从 slop 到干净的代码。