封神冰川上空,你的狮鹫轰炸机队严阵以待。食人魔从四面八方逼近——"Incoming oscars!"的战报此起彼伏。可是狮鹫要精准投弹,光知道"敌人在那边"还不够,你必须算出敌人相对你的精确角度,把这个数字喊出去,才能指挥轰炸机转到正确的方向开火。这一单元只有一关,却把数学中优雅的三角函数(trigonometry)搬进了实战:你将学会用 Math.atan2(y, x) 把一个方向向量转成角度,把令人头疼的"弧度"换算成直观的"度数",最终输出一个能指挥全军的角度值。
Math.atan2(y, x) 计算这个向量与 X 轴的夹角(弧度)——注意 Y 在前、X 在后;用 * 180 / Math.PI 把弧度转成度数;最后用 hero.say(angle) 把角度说出去指挥轰炸。这些正是游戏、机器人、AI 中"由坐标算方向"的通用数学工具。
我们平时用 (x, y) 坐标描述一个物体的位置。但"朝哪个方向开火""转多少度瞄准",描述的是方向——这时候角度才是最自然的语言。轰炸机不理解"敌人在 (100, 50)",它只听得懂"转到 30 度方向"。把"位置差"翻译成"角度",正是本关要解决的核心问题。
直角三角形里,三角函数把"角度"和"边长比例"联系起来。传说中的口诀 SOH-CAH-TOA 帮你记住三个定义:正弦(Sin)= 对边/斜边(O/H)、余弦(Cos)= 邻边/斜边(A/H)、正切(Tan)= 对边/邻边(O/A)。本关的关键是正切:已知一个方向向量的 x、y 分量(相当于邻边和对边),它们的比值 y/x 就是斜率,而由斜率反推角度,靠的正是正切的逆运算——反正切(arctangent)。
方向向量(direction vector)描述的是"从一点指向另一点"的箭头。要得到"从英雄指向敌人"的向量,只需把敌人的位置减去英雄的位置。相减的结果是一个新向量,它的 x 分量表示"敌人在我右边多少",y 分量表示"敌人在我上面多少"。这个向量既包含了方向,也包含了距离。
在 扣哒世界 里,位置对象 pos 提供了 subtract 方法,能直接做向量减法。enemy.pos.subtract(hero.pos) 返回的就是"从英雄指向敌人"的方向向量,它有 .x 和 .y 两个分量。这一步是整个角度计算的起点——先有向量,才能谈角度。
# 找到攻击的向量:敌人位置 - 英雄位置 enemy = hero.findNearestEnemy() vectorDirection = enemy.pos.subtract(hero.pos) # vectorDirection.x → 水平分量;vectorDirection.y → 垂直分量
enemy.pos.subtract(hero.pos)),得到的才是"从英雄指向敌人"的向量。如果写反成"英雄减敌人",箭头方向会完全相反,算出的角度也会差 180 度,轰炸机会朝反方向开火。
正切告诉我们 tan(θ) = y / x(对边比邻边)。现在情况反过来:我们已知向量的 x、y 分量(也就知道了斜率 y/x),想求出角度 θ。这需要正切的逆运算——反正切(arctangent,记作 atan 或 tan⁻¹)。反正切吃进一个斜率,吐出一个角度。
普通的 atan(y/x) 有个缺陷:它只把 y/x 当作一个比值,分不清向量到底在哪个象限——(1,1) 和 (-1,-1) 的比值都是 1,却指向完全相反的方向。计算机专门提供了更聪明的 Math.atan2(y, x):它分别接收 y 和 x 两个参数,因此能根据两者的正负号判断出正确的象限,返回 -π 到 π 之间覆盖全方向的准确角度(单位是弧度)。
# 用 atan2 计算向量与 X 轴的夹角(结果是弧度) angle = Math.atan2(vectorDirection.y, vectorDirection.x) # 注意:y 在前,x 在后!
atan 会丢失象限信息,几乎所有编程语言(JavaScript、Python、C……)都提供了 atan2。在游戏、机器人、图形学里,"由向量求朝向"几乎总是用 atan2。记住它,你就掌握了方向计算的万能钥匙。
这是最容易犯的错:Math.atan2 的参数顺序是 (y, x)——y 在前,x 在后!这和我们平时写坐标 (x, y) 的习惯正好相反。之所以这样设计,是因为角度定义为"对边(y)比邻边(x)"的反正切,而 atan2 沿用了 atan(y/x) 里 y 在分子的顺序。写反成 atan2(x, y) 会得到一个完全错误的角度。
atan2(vec.y, vec.x)
atan2(vec.x, vec.y)
Math.atan2 返回的角度不是我们熟悉的"度",而是弧度(radian)。弧度是数学里更"自然"的角度单位:一整圈 360 度等于 2π 弧度,半圈 180 度等于 π 弧度。所有编程语言的三角函数(sin、cos、atan2……)默认都用弧度。可惜人类读不惯弧度——"1.05 弧度"远不如"60 度"直观,所以我们需要换算。
| 度数(直观) | 弧度(数学) | 方向 |
|---|---|---|
| 0° | 0 | 正右方 +x |
| 90° | π/2 ≈ 1.57 | 正上方 +y |
| 180° | π ≈ 3.14 | 正左方 -x |
| 360° | 2π ≈ 6.28 | 转回一整圈 |
换算规则来自 π 弧度 = 180 度:所以 1 弧度 = 180/π 度。要把弧度转成度数,只需乘以 (180 / Math.PI)。本关要求最终答案必须是度数,所以在 atan2 之后一定要做这步换算,否则喊出去的是弧度,轰炸机会转到完全错误的角度。
# 把弧度换算成度数:乘以 (180 / Math.PI) angle = angle * (180 / Math.PI) # 例如:π/2 弧度 × (180/π) = 90 度
sin/cos),规则正好相反——乘以 (Math.PI / 180)。记住这一对互逆系数:180/π 弧度变度,π/180 度变弧度,方向计算就再不会晕头转向。
算出度数后,用 hero.say(angle) 把它"喊"出去。在本关的设定里,狮鹫轰炸机会听从这个角度指令,转到对应方向发起轰炸。say 在这里不只是聊天,而是把计算结果作为指令传递——你的数学运算,直接变成了对全军的作战命令。
# 说出角度,指挥狮鹫轰炸机! hero.say(angle)
这一节聚焦本关的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。
本关的整体策略是一条清晰的"计算流水线":在 while True 循环里不断找到最近的敌人,用它的位置减去英雄位置得到方向向量,用 atan2 把向量转成弧度角,再乘 180/Math.PI 换成度数,最后 say 出去指挥轰炸。没有复杂的分支和数据结构,难点全在于把数学步骤一步不差地翻译成代码——尤其是 atan2 的参数顺序和弧度转度数这两处。
每轮循环先 findNearestEnemy(),并用 if enemy: 判空——只有确实有敌人时才继续算角度。这既避免了对 None 取 pos 而报错,也符合"有目标才开火"的作战逻辑。这个判空习惯在前面几个单元里已经反复强调过。
# 关键片段:循环找敌并判空 while True: enemy = hero.findNearestEnemy() if enemy: # 有敌人,开始计算攻击角度…… pass
核心三步环环相扣:(1) 用 enemy.pos.subtract(hero.pos) 得到方向向量;(2) 用 Math.atan2(向量.y, 向量.x) 求出弧度角(y 在前);(3) 乘以 (180 / Math.PI) 把弧度换成度数。三步都不能漏、顺序不能乱,任何一处出错角度都会偏。
# 关键片段:三步算出度数角 vectorDirection = enemy.pos.subtract(hero.pos) # 1) 方向向量 angle = Math.atan2(vectorDirection.y, vectorDirection.x) # 2) 弧度角(y在前) angle = angle * (180 / Math.PI) # 3) 转成度数
最后 hero.say(angle) 把度数喊出去,狮鹫轰炸机据此转向轰炸。由于是 while True 循环,敌人位置一旦变化,下一轮会重新计算并更新角度——实时跟踪、动态瞄准,这正是循环 + 三角计算的威力。
# 关键片段:说出角度,指挥轰炸 hero.say(angle)
(x, y)(应为 (y, x));② 忘了乘 180/Math.PI、直接把弧度喊了出去。提交前务必逐字检查这两行,它们是整关的命门。
Math.atan2(y, x),y 在前;写成 (x, y) 角度全错。* 180 / Math.PI 才是关卡要的度数。enemy.pos.subtract(hero.pos);反过来向量朝向相反,差 180°。if enemy: 就取 enemy.pos,没有敌人时会报错崩溃。180/π;度转弧度才乘 π/180,别搞混。atan(y/x) 分不清象限,方向可能整体差 180°。180 不是别的数(原始注释里把它误写成 188 是笔误)。while True 里持续更新,才能实时瞄准。恭喜你指挥狮鹫轰炸机精准覆盖了来犯的食人魔,也把数学中优雅的三角函数真正用在了实战里!这一关虽小,却串起了"由坐标求方向"的完整链路。你理解了为什么需要用角度描述方向,复习了 SOH-CAH-TOA 三角口诀;学会了用敌人位置减英雄位置(subtract)得到方向向量;掌握了用 Math.atan2(y, x) 从向量算出夹角,并牢记了Y 在前、atan2 能正确分辨象限;懂得了 atan2 返回的是弧度,要乘 180 / Math.PI 转成度数;最后用 hero.say(angle) 把结果作为指令输出。这套"位置差 → 向量 → atan2 → 度数"的公式,是游戏 AI 与机器人"面向目标"的通用武器。来回顾一下核心概念:
| 核心概念 | 关键点 | 对应写法 |
|---|---|---|
| 方向向量 | 敌人位置减英雄位置 | enemy.pos.subtract(hero.pos) |
| 反正切 atan2 | 由向量求角度(弧度) | Math.atan2(vec.y, vec.x) |
| 参数顺序 | Y 在前,X 在后 | (y, x) |
| 象限判断 | 靠 x、y 符号分辨方向 | atan2 自动处理 |
| 弧度转度数 | 乘 180 除以 π | angle * (180 / Math.PI) |
| 度数转弧度 | 反向乘 π 除以 180 | angle * (Math.PI / 180) |
| 输出指挥 | 把角度喊出去 | hero.say(angle) |
| 先判空 | 有敌人才计算 | if enemy: |
懂得"把位置差翻译成方向角"——三角函数这份连接坐标与朝向的智慧,是让程序"看得懂方向、瞄得准目标"的核心力量。
封神冰川的三角试炼圆满完成,愿你带着这把数学利器,征战更广阔的算法疆场!🦅📐❄️
扣哒世界 编程知识 · 计算机科学 6(封神冰川):第十八单元
扣哒世界教研团队