<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
<title type="text">九天雁翎的博客</title>
<generator uri="https://github.com/mojombo/jekyll">Jekyll</generator>
<link rel="self" type="application/atom+xml" href="https://jtianling.com/feed.xml" />
<link rel="alternate" type="text/html" href="https://jtianling.com" />
<updated>2026-05-01T00:53:37+08:00</updated>
<id>https://jtianling.com/</id>
<author>
  <name></name>
  <uri>https://jtianling.com/</uri>
  <email></email>
</author>


<entry>
  <title type="html"><![CDATA[cross-agent-teams 发布: 让本机的多个 coding agent 互相通信]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/cross-agent-teams-release.html" />
  <id>https://jtianling.com/cross-agent-teams-release</id>
  <published>2026-05-01T21:00:00+08:00</published>
  <updated>2026-05-01T21:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;我现在本机长期开着好几个 coding agent 一起工作: Claude Code 跑主开发, Codex 在另一边做 review 或者跑长任务, 偶尔再拉一个 opencode 做点别的. 工具都不缺, 真正缺的是: 它们之间没有任何官方互通方式. 想让 Codex 把刚发现的问题”告诉”那边的 Claude Code, 只能我自己把内容复制过去. Claude Code 这种纯 TUI 的 agent 还没有”被外部唤醒”的标准入口, 你给它写一段话, 它的 CLI 不会”叮”一下, 用户必须主动切窗口才能看到.&lt;/p&gt;

&lt;p&gt;折腾了一阵以后, 我做了一个本地 MCP daemon, 叫 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross-agent-teams-mcp&lt;/code&gt; (项目内简称 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xats&lt;/code&gt;), 就是给本机所有这些 agent 一个统一的邮箱和唤醒通道. 各 agent 还跑在自己原生的 CLI 里, 完全不用改 harness, daemon 只负责”传话”和”敲门”.&lt;/p&gt;

&lt;p&gt;仓库地址: &lt;a href=&quot;https://github.com/jtianling/cross-agent-teams-mcp&quot;&gt;https://github.com/jtianling/cross-agent-teams-mcp&lt;/a&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;一句话说明&quot;&gt;一句话说明&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross-agent-teams-mcp&lt;/code&gt; 是一个本地 MCP daemon. 同一台机器上的多个 coding agent (Claude Code, Codex, opencode, cursor, …) 各自把它注册成一个 MCP server, 之后就可以按名字, team 或 role 互相发私信和广播; daemon 同时负责把消息推回给收件 agent, 让它能自己醒来读. 全程不依赖任何外部服务, 数据存在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.cross-agent-teams-mcp/data.db&lt;/code&gt; 这一个 SQLite 文件里.&lt;/p&gt;

&lt;h3 id=&quot;为什么做这个&quot;&gt;为什么做这个&lt;/h3&gt;

&lt;p&gt;我的痛点很具体, 不是”想搞一个 multi-agent 框架”:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Codex 和 Claude Code 想互相调用, 只能我手动复制粘贴. 一来一回, 上下文很容易就丢了.&lt;/li&gt;
  &lt;li&gt;Claude Code 没有”被外部唤醒”的标准入口. 你给它写一段话, 它的 CLI 不会主动提示, 用户得切回去看.&lt;/li&gt;
  &lt;li&gt;用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux send-keys&lt;/code&gt; 兜底唤醒过, 但要手动维护 pid 和 pane 的对应关系, 而且只对纯 TUI agent 管用, Claude Code 这种又是另一套.&lt;/li&gt;
  &lt;li&gt;多个 agent 之间没有共享上下文, 每开一个新会话都要把同一段背景再贴一遍.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以 xats 想做的事情非常窄: 给本机已经在跑的这些 coding agent 一个公共的 mailbox + wake 通道, 别的什么都不管.&lt;/p&gt;

&lt;h3 id=&quot;跟现有方案的区别&quot;&gt;跟现有方案的区别&lt;/h3&gt;

&lt;p&gt;我特地想跟下面这几类东西划清边界, 因为最初一拍脑袋很容易把它们混在一起:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Claude Code 自带的 sub-agent / Task tool&lt;/strong&gt;: 那是父子关系, 一个 Claude 实例 fork 出短生命周期的 sub-agent, 用完就丢, 没有跨 harness 的概念. xats 处理的是同级 agent 之间的横向沟通, 各 agent 是独立长生命周期会话.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codex --remote&lt;/code&gt;&lt;/strong&gt;: 那只是把 Codex 自己的 TUI 连到远端的 Codex 进程, 还是单 agent 在玩自己. xats 不替换任何 agent 的 runtime.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;autogen / crewai 这种 multi-agent 框架&lt;/strong&gt;: 那些是 orchestrator, 由框架自己持有 agent 循环, 你写 Python 把业务塞进去. xats 反过来 — 各 agent 仍然在它原生的 CLI 里运行 (Claude Code 还是 Claude Code 的 TUI, Codex 还是 Codex 的 TUI), MCP daemon 只负责 inbox 和 wake. 不绑定语言, 不要求把代码迁到框架里.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一句话定位: 给本机已经在跑的那些 coding agent 一个共用的消息总线, 而不是又造一个 multi-agent 框架.&lt;/p&gt;

&lt;h3 id=&quot;技术上比较有意思的两点&quot;&gt;技术上比较有意思的两点&lt;/h3&gt;

&lt;h4 id=&quot;claude-code-的-channel-proxy&quot;&gt;Claude Code 的 channel-proxy&lt;/h4&gt;

&lt;p&gt;Claude Code 有一个 experimental capability 叫 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notifications/claude/channel&lt;/code&gt;, 启动的时候得加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--dangerously-load-development-channels server:&amp;lt;name&amp;gt;&lt;/code&gt; 才会打开. 但 daemon 本身是 HTTP server, 而 Claude Code 跟 MCP server 之间走的是 stdio — 两边接口不一样, 不能直接对接.&lt;/p&gt;

&lt;p&gt;解法是单独发布一个很薄的 stdio shim, 叫 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross-agent-teams-channel&lt;/code&gt;. Claude Code 把它当普通 MCP server 启动, shim 内部建一条到 daemon 的长连接, 把 daemon 推过来的 wake 通过 channel 通道注入回 Claude Code. 用户什么都不用配, 只要在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.mcp.json&lt;/code&gt; 里同时加 daemon 和 shim 两个条目即可.&lt;/p&gt;

&lt;p&gt;这里还有一个值得记一下的细节: shim 启动时给自己生成一个 channel_session_id (csid), 并把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;process.ppid&lt;/code&gt; (= Claude Code CLI 自己的 pid) 注册到 daemon 上的 proxy 行. 之后 Claude Code 业务侧调 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_agent&lt;/code&gt; 时只要把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ui_pid=$PPID&lt;/code&gt; 一起传上去, daemon 就会按 ui_pid 找到对应的 proxy 并自动把 csid 绑过去 — 用户不需要手动复制 csid, 也避免了多个 Claude 实例之间 csid 串错的”silent mis-bind”陷阱 (这个 bug 我专门 fix 过, 排查的时候相当不直观).&lt;/p&gt;

&lt;h4 id=&quot;pid--tty--pane-的-tmux-唤醒&quot;&gt;pid → tty → pane 的 tmux 唤醒&lt;/h4&gt;

&lt;p&gt;对 opencode, cursor, 或者没开 app-server 的 Codex 这类没有 push 通道的 agent, 唤醒方式只能朴素一点: 把一行短文本 paste 进 agent 所在的 tmux pane.&lt;/p&gt;

&lt;p&gt;实现思路是: 注册时拿到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ui_pid&lt;/code&gt;, 通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/ttys&lt;/code&gt; 反查 tty, 再用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux list-panes -F&lt;/code&gt; 把 tty 映射到 pane id, 缓存下来. 后续 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send_message&lt;/code&gt; 触发 wake 时, 直接 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux send-keys&lt;/code&gt; 进对应 pane. 想法不复杂, 但踩过的坑不少: macOS 上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps&lt;/code&gt; 拿到的 tty 跟 tmux 看到的不一定字面一致; 用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx&lt;/code&gt; 套了 wrapper 后 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;process.ppid&lt;/code&gt; 不是 Claude Code 而是 npm 的 pid; 等等. 现在的实现会沿 process tree 一直往上找, 直到摸到真正的 host pid 为止.&lt;/p&gt;

&lt;h3 id=&quot;现在能用什么&quot;&gt;现在能用什么&lt;/h3&gt;

&lt;p&gt;下面这些是已经稳定, 我每天都在用的:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_agent&lt;/code&gt;: 自动判断 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-code&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codex&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;custom&lt;/code&gt; 三种 agent_type&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send_message&lt;/code&gt;: 1→1 私信, 按 name 或 agent_id&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;broadcast&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;broadcast_to_role&lt;/code&gt;: 同 team 内广播&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_inbox&lt;/code&gt;: 读邮箱&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list_agents&lt;/code&gt;: 看看团队里有谁&lt;/li&gt;
  &lt;li&gt;唤醒通道: Claude Code 走 channel push, Codex 走 app-server WebSocket (开了 app-server 的话) / 没开就 tmux paste, opencode/cursor/custom 走 tmux paste&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ui_pid&lt;/code&gt; 自动绑 channel, csid 不一致会被 daemon 主动拒绝&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外有两块功能已经实现了, 但 README 里暂时没强推, 因为我自己还没用顺手:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;共享任务列表 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task_add&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task_list&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task_claim&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task_complete&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;contracts (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_contract&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subscribe_contract&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff_contracts&lt;/code&gt;), 给跨 agent 的接口约定一个轻量描述&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;等我自己用透以后再补到 README 里.&lt;/p&gt;

&lt;h3 id=&quot;怎么开始用&quot;&gt;怎么开始用&lt;/h3&gt;

&lt;p&gt;启 daemon (起一次, 让它持续跑, 单独终端 / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchd&lt;/code&gt; 都行):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; cross-agent-teams-mcp@latest daemon &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 9100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后给 Claude Code 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.mcp.json&lt;/code&gt; 加两个条目, 一个是 HTTP 入口, 一个是 channel shim:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cross-agent-teams&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://127.0.0.1:9100/mcp&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cross-agent-teams-channel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cross-agent-teams-mcp@latest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cross-agent-teams-channel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--daemon-url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://127.0.0.1:9100/mcp&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动 Claude Code 时带上 channel loader:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude &lt;span class=&quot;nt&quot;&gt;--dangerously-load-development-channels&lt;/span&gt; server:cross-agent-teams-channel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Codex 那边稍微特殊一点. 工具调用本身只要在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.codex/config.toml&lt;/code&gt; 里加一段 streamable-http 配置就行, 没有 channel proxy 的事:&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[mcp_servers.cross-agent-teams]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;streamable-http&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;http://127.0.0.1:9100/mcp&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但如果你希望别的 agent 能”唤醒”这个 Codex thread, 而不是只能给它写邮件等它自己来读, 就要再多一步: 把 Codex 自带的 app-server 一起拉起来, 让 Codex TUI 通过它连过来. 这条路用的是 Codex 自家的 websocket 协议 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codex-appserver&lt;/code&gt; transport), wake 的本质是直接在那个 thread 里 start a turn, 比 tmux paste 干净不少:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;codex app-server &lt;span class=&quot;nt&quot;&gt;--listen&lt;/span&gt; ws://127.0.0.1:8799     &lt;span class=&quot;c&quot;&gt;# 一个终端开 app-server&lt;/span&gt;
codex &lt;span class=&quot;nt&quot;&gt;--remote&lt;/span&gt; ws://127.0.0.1:8799                &lt;span class=&quot;c&quot;&gt;# 另一个终端开 TUI 连过来&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;之后 Codex 在自己的会话里调 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_agent&lt;/code&gt; 注册一下, daemon 就会把它的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thread_id&lt;/code&gt; 记成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codex-appserver&lt;/code&gt; delivery (Codex 0.124.0+ 会自动 export &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CODEX_THREAD_ID&lt;/code&gt; 给 MCP 工具, 注册时直接用即可). 后面 xats 给这个 Codex 推消息, 走的就是 websocket 直连, 不再依赖 tmux.&lt;/p&gt;

&lt;p&gt;如果不开 app-server 也能用, daemon 会回退到 tmux paste, 只是体验上会差一点 — 唤醒得靠把一行字 paste 到 Codex 所在的 pane. 所以个人建议是: 想被同侪 agent 主动唤醒的 Codex, 就把 app-server 一起开上.&lt;/p&gt;

&lt;p&gt;opencode / cursor 这类 agent 直接连 daemon, 没有专属 wake 通道, 都是走 tmux paste, 配置都在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docs/configs/&lt;/code&gt; 下面.&lt;/p&gt;

&lt;p&gt;接下来在 agent 会话里直接说人话就够了, 不用记工具名:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;把我注册到 xats, 名字叫 alice.&lt;/p&gt;

  &lt;p&gt;给 bob 发个消息: 迁移那个事情进度怎么样了?&lt;/p&gt;

  &lt;p&gt;我邮箱里有什么新消息?&lt;/p&gt;

  &lt;p&gt;xats 上现在还有谁在?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;agent 自己会挑 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_agent&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send_message&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_inbox&lt;/code&gt; 这些工具去调.&lt;/p&gt;

&lt;h3 id=&quot;后续打算-作者考虑中-不算承诺&quot;&gt;后续打算 (作者考虑中, 不算承诺)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;短期: 把 contracts 跑顺, 给跨 agent 的接口约定一个轻量描述; 共享任务列表也希望真正用起来.&lt;/li&gt;
  &lt;li&gt;中期: 跨机器. 现在严格 localhost, 一旦扩出去, 鉴权, 隔离, proxy 这些都得重新设计.&lt;/li&gt;
  &lt;li&gt;不打算做的: agent 编排框架. xats 的边界就是 transport + inbox, agent 之间怎么协作, 交给用户自己的 prompt 和工作流去决定.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;顺便说一句-它已经在-dogfood-了&quot;&gt;顺便说一句, 它已经在 dogfood 了&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xats&lt;/code&gt; 现在已经在它自己开发流程里用上了. 这次 0.5.0 之前的好几次 release, 包括 channel proxy 的重构, 以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_*_self&lt;/code&gt; 几个工具被 collapse 成统一的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register_agent&lt;/code&gt;, 都是我用 Claude Code + Codex + xats 三方协作做掉的: Claude Code 写一部分实现, Codex 在旁边做 review 或者跑独立的检查, 互相通过 xats 同步进度和发现的问题, 不再需要我做”人肉中继”. 这条经历比我自己讲一堆”它能做什么”要更有说服力.&lt;/p&gt;

&lt;p&gt;如果你也在本机同时跑了好几个 coding agent, 又被它们之间没法互通烦到过, 欢迎试用一下, 也欢迎反馈.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/cross-agent-teams-release.html&quot;&gt;cross-agent-teams 发布: 让本机的多个 coding agent 互相通信&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on May 01, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Agent of Empires: 一个基于 tmux 的 AI agent 会话管理器]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/agent-of-empires-intro.html" />
  <id>https://jtianling.com/agent-of-empires-intro</id>
  <published>2026-03-30T15:21:39+08:00</published>
  <updated>2026-03-30T15:21:39+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近一段时间, 各种 AI coding agent 工具我都在试. 试来试去以后, 我越来越觉得, 真正麻烦的往往不是模型本身, 而是怎么把多个 agent 安排好: 谁在什么分支上工作, 谁在跑什么任务, 当前是不是卡住了, 要不要我去接管一下, 关掉界面以后它是不是还在继续跑. 这些事情如果全靠手工管理, agent 一多, 很快就乱了.&lt;/p&gt;

&lt;p&gt;用了大半个月的 &lt;a href=&quot;https://github.com/njbrake/agent-of-empires&quot;&gt;Agent of Empires&lt;/a&gt; 以后, 觉得可以认真写一下. 名字起得很大(以后简称 AoE) 不过它做的事情倒是挺朴素: 它不是重新造一整套”AI IDE”, 而是站在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git worktree&lt;/code&gt; 这些已经很好用的基础设施上, 做了一个专门给 AI agent 用的会话管理器. AoE 最近刚到 v1.0.0, 整体已经比较成熟了.&lt;/p&gt;

&lt;p&gt;仓库地址:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;原版: &lt;a href=&quot;https://github.com/njbrake/agent-of-empires&quot;&gt;https://github.com/njbrake/agent-of-empires&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;我的 fork: &lt;a href=&quot;https://github.com/jtianling/agent-of-empires&quot;&gt;https://github.com/jtianling/agent-of-empires&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;一句话说明&quot;&gt;一句话说明&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Agent of Empires&lt;/code&gt; 可以理解成一个终端里的 AI agent 调度面板. 它把不同 agent 跑在各自独立的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux session&lt;/code&gt; 里, 并且可以结合 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git worktree&lt;/code&gt; 同时在同一个项目的不同分支上开工. 目前支持的 agent 包括 Claude Code, Codex CLI, Gemini CLI, OpenCode, Cursor CLI, Copilot CLI, Mistral Vibe, Pi.dev, Factory Droid CLI 等, 基本上主流的终端 AI agent 都覆盖了.&lt;/p&gt;

&lt;h3 id=&quot;为什么我会觉得它有意思&quot;&gt;为什么我会觉得它有意思&lt;/h3&gt;

&lt;p&gt;现在做 AI 辅助编程, 比较自然的一种工作方式, 其实已经不是”我和一个 agent 一问一答”了, 而是:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一个 agent 写功能&lt;/li&gt;
  &lt;li&gt;一个 agent 跑测试或者修 lint&lt;/li&gt;
  &lt;li&gt;一个 agent 只做 code review&lt;/li&gt;
  &lt;li&gt;还有一个 agent 去查文档或者整理思路&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这种情况下, 单纯开很多终端标签页也不是不行, 但是很快就会碰到几个问题: 目录容易乱, 分支容易串, 关掉窗口以后状态不好追, 另外 agent 当前到底是在忙, 在等, 还是已经结束了, 也不容易一眼看清.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Agent of Empires&lt;/code&gt; 的思路就是把这些问题系统化处理掉. 它的几个核心点我觉得比较实用:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;基于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt;, 所以会话本身是持续存在的, 关掉 TUI 以后 agent 也不会停&lt;/li&gt;
  &lt;li&gt;能结合 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git worktree&lt;/code&gt;, 让不同 agent 在不同分支上并行工作&lt;/li&gt;
  &lt;li&gt;有 TUI, 也有 CLI, 不一定非得全程待在界面里&lt;/li&gt;
  &lt;li&gt;可以看状态(Running / Waiting / Idle), 也可以直接 attach 进去接管&lt;/li&gt;
  &lt;li&gt;支持 Docker sandbox, 想把 agent 跑得更隔离一些的话很方便&lt;/li&gt;
  &lt;li&gt;内置 diff view, 可以在 TUI 里直接 review agent 的 git 改动&lt;/li&gt;
  &lt;li&gt;支持给 agent 发消息(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aoe send&lt;/code&gt;), 不用 attach 进去也能下指令&lt;/li&gt;
  &lt;li&gt;每个项目可以有自己的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.aoe/config.toml&lt;/code&gt;, 配 hooks 和项目级设定&lt;/li&gt;
  &lt;li&gt;有声音提醒, agent 状态变化的时候可以收到通知&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;说白了, 它不是去替代终端, 而是把”同时管理很多个 agent 终端”这件事情做得更顺手.&lt;/p&gt;

&lt;p&gt;在我使用的时候, 最最让我感觉有用的是, 当我不希望 Code Agent 老是对话中断, 可以保持持续运行, 我习惯在一个做服务器使用的 Mac 电脑上, 然后哪怕我需要离开工作环境, 比如在交通的路上, 在家里, 也可以通过一个简单的ssh登录到工作的Mac上, 运行aoe, 直接就可以恢复整个工作环境. 也是因为这个优势, 我就没有使用很强大的本地Agent管理工具, 类似 cmux 这样的, 虽然本地的窗口分割, agent 管理很方便, 但是远程没法简单的恢复.&lt;/p&gt;

&lt;h3 id=&quot;安装和最基本的用法&quot;&gt;安装和最基本的用法&lt;/h3&gt;

&lt;p&gt;原版 README 里给了几种安装方式, 最简单的大概是这样:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-fsSL&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/njbrake/agent-of-empires/main/scripts/install.sh &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者如果你用 Homebrew 或 Nix:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Homebrew&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;aoe

&lt;span class=&quot;c&quot;&gt;# Nix&lt;/span&gt;
nix run github:njbrake/agent-of-empires
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动以后, 最直接的用法就是:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aoe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外它也支持直接从命令行加 session, 比如:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 在当前项目上加一个 session&lt;/span&gt;
aoe add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 在新的 worktree / 分支上加一个 session&lt;/span&gt;
aoe add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; feat/my-feature &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 用 sandbox 方式启动&lt;/span&gt;
aoe add &lt;span class=&quot;nt&quot;&gt;--sandbox&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个设计里面我最喜欢的一点, 就是它没有把底层模型藏起来. 底层还是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; session, 所以真出了什么问题, 你随时都可以 attach 进去看, 不会有那种”界面一关, 一切都变成黑盒”的感觉.&lt;/p&gt;

&lt;h3 id=&quot;它适合什么样的人&quot;&gt;它适合什么样的人&lt;/h3&gt;

&lt;p&gt;我觉得它尤其适合下面这类场景:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;已经习惯终端工作流, 至少不排斥 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;一个项目里会同时跑多个 AI agent&lt;/li&gt;
  &lt;li&gt;会认真使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git worktree&lt;/code&gt; 或多分支并行开发&lt;/li&gt;
  &lt;li&gt;希望 agent 是长期运行的, 而不是一次性问答式工具&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;反过来说, 如果你平时本来就不怎么用终端, 也不太愿意接受 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; 这一层概念, 那上手成本可能还是会觉得偏高一点. 这个工具显然不是给”完全不想碰终端”的用户准备的.&lt;/p&gt;

&lt;h3 id=&quot;我自己-fork-以后改了哪些&quot;&gt;我自己 fork 以后改了哪些&lt;/h3&gt;

&lt;p&gt;我从比较早期就开始 fork 了, 累计提交了大概 90 多个 commit. 早期加的一些功能, 比如 profile 机制, 终端标题管理, tmux 下的各种交互修复. 目前 fork 比上游多出来的改动, 主要集中在几个日常体验的细节上:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session 导航快捷键&lt;/strong&gt; – 原版在 session 之间的导航比较基础, 我加了一整套: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+,&lt;/code&gt; 在 session 之间快速切换(跨 group 循环), 在 TUI 和 tmux 里都可以用数字键(1-99)直接跳转到指定 session, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+b b&lt;/code&gt; 回到上一个 session(类似 vim 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+^&lt;/code&gt;). tmux 内部也加了 vi 风格的 pane 导航(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+b h/j/k/l&lt;/code&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+;&lt;/code&gt; 循环切换 pane, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Q&lt;/code&gt; 一键 detach. 这些快捷键加起来以后, 日常在多个 agent 之间跳转的手感好了很多, 基本不需要再回到 TUI 列表里去选.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notification Bar&lt;/strong&gt; – 灵感来自 &lt;a href=&quot;https://github.com/asheshgoplani/agent-deck&quot;&gt;Agent Deck&lt;/a&gt;, 在 tmux 的状态栏里直接显示所有 session 的实时状态(Running / Waiting / Idle), 带状态图标. 这样即使你 attach 在某一个 agent session 里工作, 也能通过状态栏一眼看到其他 session 有没有在等你输入, 不用切回 TUI 去检查. 配合 quick-switch, 跳过去的时候还会自动 ack 掉 Waiting 状态.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Restart&lt;/strong&gt; – 按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;R&lt;/code&gt; 键可以直接重启 agent pane, 不需要销毁整个 session 再重建. 主要场景是像 Claude Code 这样需要重启才能刷新 skill 配置的 agent, 改完 skill 以后按一下就行, 不用手动退出再重开. 对 Claude Code 和 Codex 还做了优雅的 resume restart, 会持久化 resume token, 重启后可以从上次的对话状态恢复.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Narrow-screen layout&lt;/strong&gt; – 在窄终端下(iPhone 竖屏, Mac 分屏, 小 tmux pane), TUI 会自动隐藏预览面板, 只显示全宽的 session 列表. Attach 到有 split pane 的 session 时, agent pane 会自动 zoom 成全屏可用的视图; 回到宽终端时自动 unzoom. Pane 切换快捷键(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+b h/j/k/l&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+;&lt;/code&gt;)是 zoom-aware 的, 在 zoom 状态下切换 pane 会自动 re-zoom 目标 pane, 体验无缝.&lt;/p&gt;

&lt;h3 id=&quot;总的感觉&quot;&gt;总的感觉&lt;/h3&gt;

&lt;p&gt;用了大半个月以后, 我觉得 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Agent of Empires&lt;/code&gt; 最有价值的地方, 不是它又支持了多少个模型, 而是它抓住了一个很实际的问题: 当 AI agent 从”偶尔用一下”变成”同时跑好几个”以后, 真正需要管理的是会话, 状态, 隔离和上下文, 而不是一个更花哨的聊天框.&lt;/p&gt;

&lt;p&gt;到 v1.0.0 以后, 完成度已经相当高了, diff view, per-repo config, 声音提醒, send message 这些功能补上以后, 日常使用基本不需要再自己折腾太多. 如果你本来就在终端里工作, 也已经开始习惯用多个 agent 并行做事, 那这个工具确实值得认真试一下.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/agent-of-empires-intro.html&quot;&gt;Agent of Empires: 一个基于 tmux 的 AI agent 会话管理器&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 30, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[dual-yazi: 给 Yazi 加上我想要的双栏模式]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/yazi-dual-pane-mode.html" />
  <id>https://jtianling.com/yazi-dual-pane-mode</id>
  <published>2026-03-17T21:13:55+08:00</published>
  <updated>2026-03-17T21:13:55+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近在终端里管理文件时, 我一直有点拧巴: 一方面, &lt;a href=&quot;https://github.com/sxyazi/yazi&quot;&gt;Yazi&lt;/a&gt; 这种新一代 terminal file manager 确实做得很好, 速度快, 预览强, 插件系统也灵活; 另一方面, 如果你像我一样长期用过 Midnight Commander, Total Commander 这类双栏文件管理器, 很多操作习惯其实已经刻在手上了.&lt;/p&gt;

&lt;p&gt;所以这两天我干脆 fork 了一份 Yazi, 做了个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dual-yazi&lt;/code&gt;: &lt;a href=&quot;https://github.com/jtianling/dual-yazi&quot;&gt;https://github.com/jtianling/dual-yazi&lt;/a&gt;. 它不是另起炉灶重写一个文件管理器, 而是在原版 Yazi 上补上我自己最想要的一层工作流: 固定双栏, 每栏独立 tabs, 直接跨栏复制/移动, 以及单栏/双栏和预览模式的切换.&lt;/p&gt;

&lt;p&gt;对我自己来说, 这是个挺自然的方向. 我喜欢 Yazi 这套比较现代的 TUI 文件管理体验, 但也一直怀念传统双栏管理器那种”左右两个目录永远摆在眼前”的直接感. 现在总算把这两边拼到一起了.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/2026/dual-yazi-screenshot.png&quot; alt=&quot;dual-yazi 双栏模式截图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;仓库地址:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Yazi 官方仓库: &lt;a href=&quot;https://github.com/sxyazi/yazi&quot;&gt;https://github.com/sxyazi/yazi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;我的 fork: &lt;a href=&quot;https://github.com/jtianling/dual-yazi&quot;&gt;https://github.com/jtianling/dual-yazi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;yazi-为什么值得折腾&quot;&gt;Yazi 为什么值得折腾&lt;/h3&gt;

&lt;p&gt;如果还没用过 Yazi, 我觉得它算是这几年 terminal 文件管理器里很值得一试的一个.&lt;/p&gt;

&lt;p&gt;官方 README 里列的点很多, 不过我自己最看重的其实是下面几件事: 它真的很快, 异步 I/O 和任务调度做得比较彻底; 预览能力很完整, 不只是看文本, 图片、PDF、压缩包、代码高亮这些都考虑到了; 另外 Lua 插件系统也足够开放, 不会让人有一种”功能很多, 但改不动”的感觉.&lt;/p&gt;

&lt;p&gt;换句话说, 它不是那种只能拿来简单 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hjkl&lt;/code&gt; 浏览目录的小工具, 而是已经能比较认真地当 daily driver 用的 terminal file manager. 我现在想做双栏, 也正是因为我觉得它本体已经足够好, 值得在这个基础上继续补自己想要的交互.&lt;/p&gt;

&lt;h3 id=&quot;我为什么还是想要双栏&quot;&gt;我为什么还是想要双栏&lt;/h3&gt;

&lt;p&gt;tab 当然有用, 单栏 + preview 其实也已经能覆盖不少场景. 但这和真正的双栏工作流还是不一样.&lt;/p&gt;

&lt;p&gt;比如整理下载目录, 对比两个目录结构, 从一个地方筛文件再搬到另一个地方, 或者一边看项目目录一边把东西归档, 双栏的心智模型就是更直接: 左边是来源, 右边是目标, 焦点在哪一栏, 操作就对哪一栏生效. 你不需要频繁在脑子里记”我刚才那个目标目录在哪个 tab 里”, 也不需要反复切来切去确认当前上下文.&lt;/p&gt;

&lt;p&gt;这也是为什么很多老文件管理器到今天依然有人喜欢. 它们不一定更现代, 但在”批量整理文件”这个问题上, 工作流非常顺手.&lt;/p&gt;

&lt;h3 id=&quot;dual-yazi-现在有什么&quot;&gt;dual-yazi 现在有什么&lt;/h3&gt;

&lt;h3 id=&quot;1-dual-pane-mode&quot;&gt;1. Dual-Pane Mode&lt;/h3&gt;

&lt;p&gt;程序启动后默认就是固定双栏模式, 左右各一个 pane, 左边默认获得焦点. 每个 pane 都有自己的 header 和 status bar, 所以它不是简单把原界面从中间切一刀, 而是把每一栏都当成一个完整的浏览上下文来处理.&lt;/p&gt;

&lt;p&gt;默认的双栏布局更偏目录浏览: 每栏显示 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parent + current&lt;/code&gt;, 也就是父目录列加当前目录列, preview 默认先隐藏. 这样做的原因很简单, 双栏模式下最常见的需求通常不是看大预览, 而是快速在两个目录之间来回操作.&lt;/p&gt;

&lt;p&gt;切换 pane 也尽量沿用了大家熟悉的习惯:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tab&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w w&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w Ctrl-w&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w h&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w l&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-per-pane-independent-tabs&quot;&gt;2. Per-Pane Independent Tabs&lt;/h3&gt;

&lt;p&gt;双栏和 tabs 不是互斥关系. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dual-yazi&lt;/code&gt; 里, 每个 pane 都独立维护自己的一组 tabs, 也各自有自己的 tab cursor 和状态. 这样一来, 你就不是只有两个固定目录, 而是相当于有两套互不干扰的工作区.&lt;/p&gt;

&lt;p&gt;这点我自己很喜欢. 比如左边可以长期放源码、文档、下载目录这几组 tab, 右边放归档、临时目录、目标目录这几组 tab. 传统双栏文件管理器的直接感还在, 但比传统双栏多了一层更灵活的组织方式.&lt;/p&gt;

&lt;p&gt;更重要的是, 原本 Yazi 的 tab 相关操作也基本保留下来了. 默认 keymap 里这一组现在是:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; 新建 tab&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-c&lt;/code&gt; 关闭当前 tab, 如果已经是最后一个 tab 则退出&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1-9&lt;/code&gt; 切换 tab&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;]&lt;/code&gt; 前后切换&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;}&lt;/code&gt; 交换 tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tab_rename&lt;/code&gt; 这个动作目前还在, 但默认没有单独分配快捷键, 需要的话可以自己在 keymap 里绑定.&lt;/p&gt;

&lt;p&gt;这些操作现在都是对当前 pane 生效, 而不是全局共享一套 tab 列表.&lt;/p&gt;

&lt;h3 id=&quot;3-cross-pane-copymove-mc-style-f5f6&quot;&gt;3. Cross-Pane Copy/Move (MC-style F5/F6)&lt;/h3&gt;

&lt;p&gt;这个是我最想补的点之一.&lt;/p&gt;

&lt;p&gt;原版 Yazi 本来就有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; 这种 yank/paste 工作流, 其实很好用. 但如果你已经习惯了双栏管理器里”左边选中, 直接 F5 复制到右边; 或者 F6 直接移动到右边”这种操作, 还是会希望它是一个一键动作, 而不是先 yank, 再切 pane, 再 paste.&lt;/p&gt;

&lt;p&gt;这里直接提供了:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt;: 复制当前选中项到另一栏当前目录&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt;: 移动当前选中项到另一栏当前目录&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;它们走的是直接跨栏操作, 不经过 yank register. 如果当前没有显式 selection, 就对 hover 的那个文件生效. 这样做的好处是, 它不会打断原来 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; 的使用习惯, 但在双栏场景里又能提供更像 MC 的肌肉记忆.&lt;/p&gt;

&lt;p&gt;顺手也把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F7&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt; 对上了更传统的语义: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F7&lt;/code&gt; 创建文件或目录, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt; 删除到回收站; 如果你想永久删除, 还是用原本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;D&lt;/code&gt;. 这一套按下来, 终端里的文件整理体验确实会更像老派双栏管理器.&lt;/p&gt;

&lt;h3 id=&quot;4-singledual-toggle&quot;&gt;4. Single/Dual Toggle&lt;/h3&gt;

&lt;p&gt;虽然我大部分时候想要双栏, 但也不是任何时候都必须同时看到两边.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w o&lt;/code&gt; 用来在双栏和单栏之间切换. 这里我比较在意的一点是: 切成单栏时, 另一栏不是被重置, 而只是隐藏. 它原来的目录、cursor、selection、history 都会保留下来. 再切回双栏时, 状态会原样恢复.&lt;/p&gt;

&lt;p&gt;这意味着单栏模式不是”退出双栏”, 而更像”暂时把注意力集中到当前这一栏”. 需要全宽查看内容的时候可以单栏, 需要继续做双目录操作的时候再切回双栏, 不会有上下文丢失的问题.&lt;/p&gt;

&lt;p&gt;另外在单栏模式下, pane 的概念其实还在. 你仍然可以切换 active pane, 也仍然可以把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 的目标指向那个当前隐藏着的 pane. 这一点我自己觉得挺实用.&lt;/p&gt;

&lt;h3 id=&quot;5-preview-toggle&quot;&gt;5. Preview Toggle&lt;/h3&gt;

&lt;p&gt;双栏模式默认优先目录浏览, 但有时候还是会想临时看一下预览.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-w p&lt;/code&gt; 用来切换双栏下的两种布局:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;目录模式: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parent + current&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;预览模式: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;current + preview&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;也就是说, 你不需要退出双栏, 就能把两边同时切成更偏”查看内容”的布局. 平时整理目录时用目录模式, 需要确认文件内容时切到预览模式, 这两种都保留在双栏工作流里.&lt;/p&gt;

&lt;p&gt;我感觉这比只保留一种固定双栏布局更顺手. 不然的话, 双栏很容易变成一个只能”搬文件”的模式, 而不是一个真正能长期使用的主界面.&lt;/p&gt;

&lt;h3 id=&quot;6-undoredo&quot;&gt;6. Undo/Redo&lt;/h3&gt;

&lt;p&gt;这个功能也很关键.&lt;/p&gt;

&lt;p&gt;双栏管理器里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 当然很顺手, 但真要放心大胆地整理文件, 还是得有”后悔药”. 现在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dual-yazi&lt;/code&gt; 已经支持 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt; 撤销和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-r&lt;/code&gt; 重做. 不只是 rename、create、copy、move, trash 这些普通文件操作能撤销, 连双栏里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy_to&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;move_to&lt;/code&gt; 也会进 undo 栈.&lt;/p&gt;

&lt;p&gt;当然, 这个 undo 目前也不是万能的. 像永久删除 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;D&lt;/code&gt;, shell command, bulk rename, plugin 自己做的操作, 现在都不在这套撤销范围里. 不过对于我最常用的那批文件整理动作来说, 已经很够用了.&lt;/p&gt;

&lt;h3 id=&quot;怎么试&quot;&gt;怎么试&lt;/h3&gt;

&lt;p&gt;目前这还是一个 fork, 我暂时没有单独给它做完整 release 流程. 如果你本来就会从源码装 Rust 项目, 直接试就行:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/jtianling/dual-yazi.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;dual-yazi
cargo &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; ./yazi-fm
yazi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你已经在用原版 Yazi, 也完全可以把它当成一个实验分支来试. 喜不喜欢双栏工作流, 基本上用半小时就能判断出来.&lt;/p&gt;

&lt;p&gt;总之, 我不是想证明”双栏一定比单栏先进”, 而是想把一种我自己已经用了很多年的文件管理习惯, 补回到一个我本来就很喜欢的现代工具里. 对我来说, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dual-yazi&lt;/code&gt; 更像是把 Yazi 往我自己的 daily workflow 推近了一步.&lt;/p&gt;

&lt;p&gt;如果你也喜欢 Yazi 的速度、预览和插件能力, 但同时又舍不得双栏管理器那种非常直接的操作感, 可以试试看这份 fork.&lt;/p&gt;

&lt;h3 id=&quot;为什么是-fork-而不是-plugin&quot;&gt;为什么是 fork, 而不是 plugin&lt;/h3&gt;

&lt;p&gt;顺便说一下, 一开始我其实是想用 Yazi 自带的 plugin 机制来做这件事的. plugin 路线对用户更友好, 升级也不用跟着主线 rebase, 怎么看都应该是首选方案.&lt;/p&gt;

&lt;p&gt;但真动手之后才发现, Yazi 的 plugin 本质上是跑在 Rust 状态机之上的 Lua 脚本: 能画 UI, 能拼装已有命令, 但动不了底层的数据结构, 也没法新增命令. 而我想要的”每栏独立 tabs、跨栏一键复制/移动、单双栏切换、双栏下切预览、undo/redo”这些, 基本上每一项都要动到底层, 不是改个布局就够的.&lt;/p&gt;

&lt;p&gt;社区其实也有纯 plugin 的尝试, 做法是把现有的两个 tab 并排画. 视觉上像双栏, 但”切栏”其实是切 tab, 跨栏复制只能靠”切过去 → 粘贴 → 切回来”模拟, 肉眼能看见抖动, 也没办法真正做到两套独立 tabs. 更像一个”双视图 viewer”, 而不是真正的双栏文件管理器.&lt;/p&gt;

&lt;p&gt;所以最后还是选择了 fork. 代价是要自己维护和主线的同步, 但换来的是每一块交互都能做到我想要的样子. 对我来说, 这个取舍是值得的.&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/yazi-dual-pane-mode.html&quot;&gt;dual-yazi: 给 Yazi 加上我想要的双栏模式&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 17, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[只给 Spec, 不给代码: 也许这会成为一种新的软件发布方式]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/spec-first-software-release.html" />
  <id>https://jtianling.com/spec-first-software-release</id>
  <published>2026-03-14T21:52:56+08:00</published>
  <updated>2026-03-14T21:52:56+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近看到 &lt;a href=&quot;https://github.com/openai/symphony&quot;&gt;openai/symphony&lt;/a&gt; 这个项目, 我第一反应不是”这个实现我想试试”, 而是”这个发布方式有点意思”.&lt;/p&gt;

&lt;p&gt;因为它在 README 里给出的第一个选项, 不是先教你怎么安装官方实现, 而是先让你根据 &lt;a href=&quot;https://github.com/openai/symphony/blob/main/SPEC.md&quot;&gt;SPEC.md&lt;/a&gt; 自己做一个. 官方当然也附了一个实验性的 Elixir 参考实现, 但那个被放在了第二个选项. 这个顺序本身就很说明问题: openai 真正想先传递出去的, 可能不是某一份具体代码, 而是这套系统的设计.&lt;/p&gt;

&lt;p&gt;我感觉这件事情挺值得记一下. 随着 code agent 越来越强, 以后有些软件的”发布”, 也许真的不一定非得先附带一份完整源码, 而是可以先给出一份足够明确的 spec, 让大家各自用自己熟悉的语言和运行环境去实现. 代码还是会有, 但它开始更像是 spec 的一个实例, 而不是唯一的交付物.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;为什么我会觉得这件事很有意思&quot;&gt;为什么我会觉得这件事很有意思&lt;/h3&gt;

&lt;p&gt;以前我们看到一个开源项目, 默认的理解通常是: 仓库里的源码就是产品本体, 文档只是帮助你理解和使用它. 就算一个项目有协议, 语言标准或者设计文档, 大多数时候也是配角.&lt;/p&gt;

&lt;p&gt;但 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symphony&lt;/code&gt; 这次给我的感觉不太一样. 它的 README 直接把 “Option 1. Make your own” 放在前面, 然后指向那份很长的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SPEC.md&lt;/code&gt;. 这就不是”如果你有兴趣, 也可以看看设计文档”了, 而是很明确地把”按照这个 spec 自己实现”当成一个正式的入口.&lt;/p&gt;

&lt;p&gt;这个姿态变化挺有意思的. 因为它隐含着一个新前提: 对一部分软件来说, “实现” 这件事情本身正在变便宜, 便宜到作者已经可以默认读者不一定非得使用官方代码, 也可以直接把作者的思想搬到自己熟悉的技术栈里.&lt;/p&gt;

&lt;p&gt;虽然吧, openai 还能通过这种方式让你消耗多一点的 Token, 一举两得.&lt;/p&gt;

&lt;h3 id=&quot;为什么这件事在今天开始成立&quot;&gt;为什么这件事在今天开始成立&lt;/h3&gt;

&lt;p&gt;我觉得最核心的变量, 还是 code agent 变强了.&lt;/p&gt;

&lt;p&gt;如果放在前些年, “你自己照着 spec 实现一个” 这句话听起来更像玩笑. 因为哪怕 spec 写得再清楚, 从文档到工程代码之间, 还是隔着大量机械而繁琐的实现劳动. 很多人不是不理解设计, 而是没有精力把它完整落出来.&lt;/p&gt;

&lt;p&gt;但现在情况已经开始不一样了. 尤其像 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symphony&lt;/code&gt; 这种项目, 它的核心价值其实并不在某个特别难复现的底层算法, 而是在一套系统边界划分和工作流设计上: 问题是什么, 目标是什么, 有哪些核心组件, 域模型是什么, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WORKFLOW.md&lt;/code&gt; 这种仓库内契约怎么组织, 配置怎么解析, agent runner 和 issue tracker 怎么配合. 这些东西一旦写进 spec, 就已经把最重要的”思想骨架”交代得差不多了.&lt;/p&gt;

&lt;p&gt;剩下的代码, 很多时候更像是把这份骨架翻译成某一种语言和工程风格. 而”翻译” 这件事情, 恰恰是 code agent 现在越来越擅长做的.&lt;/p&gt;

&lt;h3 id=&quot;这和以前的标准还不太一样&quot;&gt;这和以前的”标准”还不太一样&lt;/h3&gt;

&lt;p&gt;当然, “先有规范, 后有实现” 并不是什么新鲜事. 协议, 语言标准, 文件格式标准, 以前一直都有. 但我觉得这次不太一样的地方在于, 它不是某个成熟生态经过多年沉淀以后, 再慢慢整理出一份标准文档.&lt;/p&gt;

&lt;p&gt;它更像是一开始就在把 spec 当成第一交付物.&lt;/p&gt;

&lt;p&gt;而且这里还多了一个以前没有那么强的前提: 发布者已经可以合理假设, 读者身边有一个足够能干的 coding agent, 可以把 spec 迅速翻成一个能跑的版本. 也就是说, 这份 spec 面向的, 并不只是耐心读文档的人类工程师, 还面向一个随时可以开工的实现代理.&lt;/p&gt;

&lt;p&gt;这就让 “spec” 从过去那种偏静态, 偏归档的文档, 变成了一种可以被直接执行的发布介质.&lt;/p&gt;

&lt;h3 id=&quot;我觉得以后可能会出现什么&quot;&gt;我觉得以后可能会出现什么&lt;/h3&gt;

&lt;p&gt;我现在越来越觉得, 以后有一类软件项目, 可能真的会逐渐走向这种 “spec-first” 的发布方式.&lt;/p&gt;

&lt;p&gt;也就是作者发布的核心内容会变成:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一份足够明确的 spec&lt;/li&gt;
  &lt;li&gt;一份说明系统边界和约束的工作流/提示词&lt;/li&gt;
  &lt;li&gt;一组可以验证行为的测试或者样例&lt;/li&gt;
  &lt;li&gt;也许再附一个参考实现, 但它不一定是唯一实现&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然后使用者这边, 就不再只是 “git clone 然后跑起来”, 而是”把 spec 交给我自己的 agent, 让它按我熟悉的语言, 框架, 部署方式生成一个版本”.&lt;/p&gt;

&lt;p&gt;这件事的好处其实不少.&lt;/p&gt;

&lt;p&gt;第一, 不同团队可以更自然地贴合自己的技术栈. 有人想用 Elixir, 有人想用 Go, 有人想用 Rust, 有人甚至只想把核心思想吸收到自己现有系统里, 都不必被官方实现绑定得太死.&lt;/p&gt;

&lt;p&gt;第二, 作者传播的重点会从”这一份代码”转向”这一套设计”. 这样的话, 项目的影响力有时候反而可能更大, 因为别人采纳的不只是仓库, 而是背后的方法论.&lt;/p&gt;

&lt;p&gt;第三, 对很多 agent 时代的新工具来说, 参考实现的寿命未必像以前那么重要. 真正持久的, 可能是 spec, 测试和工作流约束; 具体代码则可以不断被本地化, 重写, 替换.&lt;/p&gt;

&lt;h3 id=&quot;当然-这也不是所有软件都适合&quot;&gt;当然, 这也不是所有软件都适合&lt;/h3&gt;

&lt;p&gt;不过我觉得这条路也不是哪里都能用.&lt;/p&gt;

&lt;p&gt;如果一个项目的核心价值在于非常细致的性能优化, 极重的工程积累, 某种很难被文字完全描述的交互体验, 那么光有 spec 显然不够. 还有一些东西, 比如模型权重, 特定训练结果, 特定数据集加工产物, 也不是”给个 spec 就能自己做一个”的.&lt;/p&gt;

&lt;p&gt;所以更可能先变成 spec-first 的, 我猜会是这几类东西:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;orchestration / workflow 类工具&lt;/li&gt;
  &lt;li&gt;协议和服务胶水层&lt;/li&gt;
  &lt;li&gt;很多企业内部其实只想落地思想, 不一定执着于官方实现的系统&lt;/li&gt;
  &lt;li&gt;本来就很强调 contract 的基础设施组件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外, 如果真的要让 spec 变成主要交付物, 我觉得以后还得配套更多东西, 比如 conformance test, 标准样例输入输出, 甚至针对 agent 的实现提示. 不然 spec 写得再长, 不同实现之间还是很容易漂.&lt;/p&gt;

&lt;h3 id=&quot;一个我觉得很新鲜的变化&quot;&gt;一个我觉得很新鲜的变化&lt;/h3&gt;

&lt;p&gt;以前 Linus Torvalds 说 “talk is cheap, show me the code”. 这句话放到今天可能就不太对了. 因为, “code is cheap”. 在 code agent 逐渐成熟以后, 也许会开始出现另一种情况: 真正稳定的部分不一定是某一份源码, 而是源码背后的 spec.&lt;/p&gt;

&lt;p&gt;源码变成了 spec 的一个瞬时投影, 可以用这个语言实现, 也可以用那个语言再实现一次; 可以今天这样组织, 明天换一套框架重写. 只要 spec 和行为约束还在, 作者真正想表达的东西就还在.&lt;/p&gt;

&lt;p&gt;如果真往这个方向发展, 那以后”发布一个软件”这件事情本身, 可能都会和今天不太一样. 发布的不只是代码, 而是设计, 是约束, 是一份足够清晰到可以让 agent 去继续生产代码的说明书.&lt;/p&gt;

&lt;p&gt;我觉得这件事挺有意思的. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symphony&lt;/code&gt; 当然还远远不是这个趋势的终点, 甚至它自己也仍然带着参考实现. 但至少它已经把一种以前不太像”正式发布方式”的东西, 很自然地摆到了台前.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/spec-first-software-release.html&quot;&gt;只给 Spec, 不给代码: 也许这会成为一种新的软件发布方式&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 14, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[net-use 发布：监控 macOS app 实际访问了哪些 IP]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/net-use-release.html" />
  <id>https://jtianling.com/net-use-release</id>
  <published>2026-03-13T14:52:52+08:00</published>
  <updated>2026-03-13T14:52:52+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;有时候想给某个 app 配防火墙白名单, 最麻烦的不是配规则, 而是根本不知道它到底连了哪些地址. 而且现在很多 app 都不只是一个主进程, 还会拉起 helper, renderer, crash reporter 之类的子进程, 只盯着一个 PID 往往不够. 于是写了个小工具 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net-use&lt;/code&gt;, 用来实时追踪指定 app 及其整个子进程树访问过的远端 IP, 并把结果去重输出出来.&lt;/p&gt;

&lt;p&gt;仓库地址：&lt;a href=&quot;https://github.com/jtianling/net-use&quot;&gt;https://github.com/jtianling/net-use&lt;/a&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;一句话说明&quot;&gt;一句话说明&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net-use&lt;/code&gt; 是一个 macOS 上的应用网络连接监控工具. 它基于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proc_pidfdinfo&lt;/code&gt; 系统调用枚举 socket 信息, 能实时抓到指定 app 以及它的所有子进程访问过的 TCP/UDP 远端地址. 为了更适合防火墙白名单这个场景, IPv4 默认会聚合成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/24&lt;/code&gt; 子网, IPv6 则保留完整地址.&lt;/p&gt;

&lt;h3 id=&quot;为什么做这个&quot;&gt;为什么做这个&lt;/h3&gt;

&lt;p&gt;我自己的需求很简单, 就是想知道一个 app 实际在访问什么地方, 并且把结果整理成一份后续可直接使用的白名单.&lt;/p&gt;

&lt;p&gt;如果只是临时看一下网络连接, 其实有很多工具能凑合用. 但是一旦要落到具体 app 上, 尤其是桌面 app 还会拉起很多子进程的时候, 事情就开始变麻烦了: 进程会变, PID 会变, 连接是动态出现的, 还要自己做去重. 所以干脆写了一个专门做这件事情的小工具.&lt;/p&gt;

&lt;h3 id=&quot;使用&quot;&gt;使用&lt;/h3&gt;

&lt;p&gt;安装:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;net-use
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最简单的用法是直接进入 TUI 模式:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;net-use
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;启动后可以浏览本机已安装 app, 输入文字筛选, 回车选中以后开始监控. 监控过程中支持导出到文件、复制到剪贴板、切换排序方式, 也可以在子网聚合显示和原始 IP 显示之间切换.&lt;/p&gt;

&lt;p&gt;如果你不想进界面, 也可以直接走 CLI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 按 Bundle ID 监控&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;net-use &lt;span class=&quot;nt&quot;&gt;--bundle&lt;/span&gt; com.google.Chrome &lt;span class=&quot;nt&quot;&gt;--no-tui&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 按进程名监控&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;net-use &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; curl &lt;span class=&quot;nt&quot;&gt;--no-tui&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 按 PID 监控&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;net-use &lt;span class=&quot;nt&quot;&gt;--pid&lt;/span&gt; 1234 &lt;span class=&quot;nt&quot;&gt;--no-tui&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出是去重后的地址列表, 例如:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;142.250.80.0/24
172.217.14.0/24
2607:f8b0:4004:800::200e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样拿去做白名单就比较直接了.&lt;/p&gt;

&lt;h3 id=&quot;另外几个我觉得比较有用的点&quot;&gt;另外几个我觉得比较有用的点&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;支持监控尚未启动的 app, 检测到启动后自动开始采集&lt;/li&gt;
  &lt;li&gt;app 退出后历史数据不会丢, 下次再出现会继续累加&lt;/li&gt;
  &lt;li&gt;可以暂停/恢复监控&lt;/li&gt;
  &lt;li&gt;支持把历史结果持久化到文件&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;限制&quot;&gt;限制&lt;/h3&gt;

&lt;p&gt;这个工具目前只支持 macOS, 并且需要 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;, 因为读取进程 socket 信息本身就需要权限.&lt;/p&gt;

&lt;p&gt;另外它是基于轮询实现的, 默认 200ms 一次, 所以生命周期特别短的连接理论上还是可能漏掉. 再有就是某些通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchd&lt;/code&gt; 拉起的 XPC service, 不一定能完全落在同一棵进程树里, 这也是目前的一个限制.&lt;/p&gt;

&lt;p&gt;总之, 如果你也有“一个 app 到底连了哪些 IP”这种需求, 可以试试看. 对我自己来说, 至少终于不用一边盯 Activity Monitor, 一边再手动整理白名单了.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/net-use-release.html&quot;&gt;net-use 发布：监控 macOS app 实际访问了哪些 IP&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 13, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[skillsmgr 发布]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/skillsmgr-release.html" />
  <id>https://jtianling.com/skillsmgr-release</id>
  <published>2026-02-03T00:00:00+08:00</published>
  <updated>2026-02-03T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近在整理各种 AI 编程工具的 skills 时，发现每个工具都有自己的目录和格式，切来切去很容易乱。索性就做了一个统一管理的小工具：skillsmgr。它把 skills 集中安装到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.skills-manager/&lt;/code&gt; 里，然后通过一个统一的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.agents/skills/&lt;/code&gt; 目录部署到项目中，目前支持 44 个工具。&lt;/p&gt;

&lt;p&gt;仓库地址：&lt;a href=&quot;https://github.com/jtianling/skills-manager&quot;&gt;https://github.com/jtianling/skills-manager&lt;/a&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;一句话说明&quot;&gt;一句话说明&lt;/h3&gt;

&lt;p&gt;skillsmgr 是一个统一的 skills 管理器，安装一次 skills 到本地仓库，然后按需部署到任意项目或全局，支持 44 个 AI 编程工具。&lt;/p&gt;

&lt;h3 id=&quot;亮点&quot;&gt;亮点&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;中心仓库，随处部署&lt;/strong&gt; — Skills 安装一次到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.skills-manager/&lt;/code&gt;，之后用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt; 交互式选择并部署到任意项目或全局，不需要每次都记住原始仓库地址。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;自定义分组批量管理&lt;/strong&gt; — 把自己的 skills 组织到命名分组里（如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--group my-tools&lt;/code&gt;），一条 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add --group&lt;/code&gt; 命令就能把整组部署到项目中。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zip 压缩包支持&lt;/strong&gt; — 可以直接从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zip&lt;/code&gt; 文件或 Anthropic 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.skill&lt;/code&gt; 包安装 skills，方便在 GitHub 之外打包和分享。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;部署模型&quot;&gt;部署模型&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;project/
├── .agents/
│   └── skills/
│       ├── code-review -&amp;gt; ~/.skills-manager/official/anthropic/skills/code-review
│       └── example-skill -&amp;gt; ~/.skills-manager/custom/example-skill
├── .claude/
│   └── skills -&amp;gt; ../.agents/skills
└── .cursor/
    └── skills -&amp;gt; ../.agents/skills
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所有 skills 统一部署到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.agents/skills/&lt;/code&gt;。原生支持该目录的工具直接读取，其他工具通过 symlink bridge 桥接到各自的 skills 目录。也可以用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--copy&lt;/code&gt; 改为复制模式。&lt;/p&gt;

&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx skillsmgr setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;使用&quot;&gt;使用&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 安装官方 Anthropic skills&lt;/span&gt;
npx skillsmgr &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;anthropic

&lt;span class=&quot;c&quot;&gt;# 从 GitHub 仓库安装&lt;/span&gt;
npx skillsmgr &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;owner/repo

&lt;span class=&quot;c&quot;&gt;# 从本地目录或 zip 安装&lt;/span&gt;
npx skillsmgr &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; ./my-skill
npx skillsmgr &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; ./skills-archive.zip

&lt;span class=&quot;c&quot;&gt;# 进入项目并交互式部署&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;your-project
npx skillsmgr init

&lt;span class=&quot;c&quot;&gt;# 添加特定 skill 到特定工具&lt;/span&gt;
npx skillsmgr add code-review &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; claude-code

&lt;span class=&quot;c&quot;&gt;# 全局部署&lt;/span&gt;
npx skillsmgr add code-review &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; claude-code

&lt;span class=&quot;c&quot;&gt;# 查看已部署的 skills&lt;/span&gt;
npx skillsmgr list &lt;span class=&quot;nt&quot;&gt;--deployed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;支持的工具&quot;&gt;支持的工具&lt;/h3&gt;

&lt;p&gt;目前支持 44 个 AI 编程工具，交互选择器中展示 16 个主流工具（Claude Code、Codex、Cursor、Gemini CLI、GitHub Copilot、Cline、Windsurf 等），其余 28 个可通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-a&lt;/code&gt; 参数直接指定。原生工具直接读取 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.agents/skills/&lt;/code&gt;，非原生工具通过 symlink bridge 桥接。&lt;/p&gt;

&lt;h3 id=&quot;主要功能&quot;&gt;主要功能&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;统一管理：所有 skills 安装到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.skills-manager/&lt;/code&gt;，分为 official / community / custom 三类&lt;/li&gt;
  &lt;li&gt;多工具部署：一份 skills 通过统一目录 + symlink bridge 部署到多个工具&lt;/li&gt;
  &lt;li&gt;默认软链接：skill 更新自动同步，也支持 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--copy&lt;/code&gt; 复制模式&lt;/li&gt;
  &lt;li&gt;自定义分组：通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--group&lt;/code&gt; 批量管理和部署自己的 skill 集合&lt;/li&gt;
  &lt;li&gt;交互式选择器：支持搜索、分组折叠、vim 风格快捷键&lt;/li&gt;
  &lt;li&gt;全局部署：用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-g&lt;/code&gt; 部署到工具的用户级目录&lt;/li&gt;
&lt;/ul&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/skillsmgr-release.html&quot;&gt;skillsmgr 发布&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on February 03, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[rulesmgr 发布：一份规则，多工具同步]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/rules-manager-release.html" />
  <id>https://jtianling.com/rules-manager-release</id>
  <published>2026-02-03T00:00:00+08:00</published>
  <updated>2026-02-03T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近在使用 Claude Code、Cursor、Cline 之类的 AI 编程工具时，总会遇到一个重复劳动：每个工具都有自己的规则目录，格式略有差异，团队的约定需要在多个地方复制粘贴，还容易漂移。于是做了一个小工具 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rulesmgr&lt;/code&gt;，统一管理一份规则，然后一次性部署到多个工具。&lt;/p&gt;

&lt;p&gt;仓库地址：&lt;a href=&quot;https://github.com/jtianling/rules-manager&quot;&gt;https://github.com/jtianling/rules-manager&lt;/a&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;一句话说明&quot;&gt;一句话说明&lt;/h3&gt;

&lt;p&gt;维护一份规则模板，选择目标工具后自动生成对应目录结构，并支持复制模式的同步更新。&lt;/p&gt;

&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx rulesmgr setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它会在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.rules-manager/&lt;/code&gt; 生成示例规则模板，结构类似下面这样：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/.rules-manager/
├── 01-tech-stack.md
├── 02-coding-principles.md
├── 03-architecture.md
├── 04-testing.md
├── 05-git-commit.md
├── 06-code-review.md
└── languages/
    ├── typescript-coding-style.md
    ├── python-coding-style.md
    └── ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;部署到项目&quot;&gt;部署到项目&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 交互模式&lt;/span&gt;
npx rulesmgr init

&lt;span class=&quot;c&quot;&gt;# 指定工具与语言&lt;/span&gt;
npx rulesmgr init &lt;span class=&quot;nt&quot;&gt;--tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;claude-code,cursor &lt;span class=&quot;nt&quot;&gt;--lang&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;typescript

&lt;span class=&quot;c&quot;&gt;# 使用复制模式&lt;/span&gt;
npx rulesmgr init &lt;span class=&quot;nt&quot;&gt;--tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;claude-code &lt;span class=&quot;nt&quot;&gt;--copy&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 部署 .gitignore&lt;/span&gt;
npx rulesmgr init &lt;span class=&quot;nt&quot;&gt;--gitignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;同步复制模式的规则&quot;&gt;同步复制模式的规则&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx rulesmgr &lt;span class=&quot;nb&quot;&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;支持的工具&quot;&gt;支持的工具&lt;/h3&gt;

&lt;p&gt;当前支持 Claude Code、Cursor、Cline、Roo Code、Kilo Code、Windsurf、OpenCode、TRAE、Goose、Antigravity 等工具，具体目录映射可参考仓库 README。&lt;/p&gt;

&lt;p&gt;欢迎试用和反馈，后续会继续补充更多工具与模板。&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/rules-manager-release.html&quot;&gt;rulesmgr 发布：一份规则，多工具同步&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on February 03, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Ollama rerank adapter]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/ollama-rerank-adapter.html" />
  <id>https://jtianling.com/ollama-rerank-adapter</id>
  <published>2025-12-17T00:00:00+08:00</published>
  <updated>2025-12-17T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;A lightweight HTTP service that wraps Ollama’s Rerank model into a standard Rerank API, enabling Dify and other applications to use local Ollama models for document reranking.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jtianling/dify-ollama-rerank-adapter&quot;&gt;https://github.com/jtianling/dify-ollama-rerank-adapter&lt;/a&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/ollama-rerank-adapter.html&quot;&gt;Ollama rerank adapter&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on December 17, 2025.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[给 DeepSeek 网站加个搜索功能的Chrome插件]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/add-search-to-deepseek.html" />
  <id>https://jtianling.com/add-search-to-deepseek</id>
  <published>2025-12-08T00:00:00+08:00</published>
  <updated>2025-12-08T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;DeepSeek 很好用, 但是他们公司一直不注重使用体验的优化, 比如在 DeepSeek 网站中, 就一直没有一个搜索历史记录的功能, 于是, 我做了一个. 没有上架商店, 开源自取.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jtianling/deepseek-search-chrome&quot;&gt;https://github.com/jtianling/deepseek-search-chrome&lt;/a&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/add-search-to-deepseek.html&quot;&gt;给 DeepSeek 网站加个搜索功能的Chrome插件&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on December 08, 2025.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[编程语言语法比较网站]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/programming-language-comparison.html" />
  <id>https://jtianling.com/programming-language-comparison</id>
  <published>2025-12-02T00:00:00+08:00</published>
  <updated>2025-12-02T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;一直以来, 经常需要同时使用多种编程语言, 要么是自己在自学一个新的编程语言, 正在做一些练手的项目, 同时公司的项目也在开发.  或者在某个项目中, 同时写前后端, 而前后端用的分别是不同的语言, 这些时候, 我都有一个想法, 要是有个网站, 能并排列出我正在使用的几个语言的语法示例就好了, 这些在切换编程语言的时候, 脑子不容易乱.  这么多年过去了, 因为大模型的成熟, 写这么一个网站变得如此的容易, 我把网址贴出来, 有一样需求的人随时查看吧.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.jtianling.com/programming-language-comparison&quot;&gt;https://www.jtianling.com/programming-language-comparison&lt;/a&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/programming-language-comparison.html&quot;&gt;编程语言语法比较网站&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on December 02, 2025.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Rust 的交叉编译]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/cross-compile-with-rust.html" />
  <id>https://jtianling.com/cross-compile-with-rust</id>
  <published>2022-05-18T00:00:00+08:00</published>
  <updated>2022-05-18T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt; 作为编译型的语言, 交叉编译挺方便的, 这样开发和部署, 都能简单挺多. 本文以在 Mac 上, 交叉编译一个使用 SDL 库的程序到一个手持 ARM 设备(&lt;a href=&quot;https://www.clockworkpi.com/gameshell&quot;&gt;clockwork Gameshell&lt;/a&gt;) 为例, 记录一下怎么使用 Rust 的交叉编译, 特别是怎么在交叉编译的时候, 还能链接类似 SDL 这种外部的库.
Rust 的生态是比较完善的, 只是相关的资料比较少的, 基本上是一步一个坑. 除了对 Rust 自身的 Rustup 等工具的了解, 还需要用到 &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;, Linux 包管理等知识, 希望对同样被困住的同学有帮助. 同时, 本文也会顺便讲讲思路, 以帮助大家将相关知识应用到其他交叉编译的场景.&lt;/p&gt;

&lt;!-- more --&gt;
&lt;h1 id=&quot;环境&quot;&gt;环境&lt;/h1&gt;
&lt;p&gt;先厘清几个术语, 在交叉编译的时候, 用于编译的机器, 叫做 Host(宿主), 这里用的是我的 Mac(12.1), 用来运行程序的机器叫做 Target(目标设备), 例子中是前面提到的 Clockwork GameShell, 一个 ARM 设备.  我们的目标就是在宿主机器上编译, 然后直接在目标设备上运行编译好的程序.&lt;br /&gt;
这种方式的好处是宿主可以是台式机, 编译速度快, 而目标设备可以是任意设备, 包括速度很慢, 不太适合执行编译任务的嵌入式设备.&lt;/p&gt;

&lt;h1 id=&quot;最简单的情况&quot;&gt;最简单的情况&lt;/h1&gt;
&lt;p&gt;假如你并不需要用其他外部库, 那可以直接使用 Rust 内置的交叉编译功能, 使用还挺简单的. 
你可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustup target list&lt;/code&gt; 列出当前系统支持的所有交叉编译目标, 在我的 Mac 上运行以后, 有 86 个之多, 其中会有一个默认的. 显示&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x86_64-apple-darwin (installed)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个也是我们现在本机用的.&lt;br /&gt;
这么多编译目标, 那我们应该用哪一个呢? 这里有个&lt;a href=&quot;https://doc.rust-lang.org/nightly/rustc/platform-support.html&quot;&gt;官方的列表&lt;/a&gt;, 分为几个支持的级别, 可以直接过去看.&lt;/p&gt;

&lt;p&gt;同样是 Linux 和 ARM, 选择也不少. 可以在目标设备上, 用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uname -a&lt;/code&gt; 命令看到一部分信息.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ uname -a
Linux clockworkpi 5.3.6-clockworkpi-cpi3 #1 SMP Tue Oct 15 17:26:44 CST 2019 armv7l GNU/Linux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;起码知道了是 armv7, 但是 armv7 的 target 还有几个, 后来我想到的办法是, 在目标设备上安装 Rust, 然后通过前面的命令来看.  当然, 前提是目标设备也能运行 Rust 才行, 要是不行的话, 那就只能查资料和尝试了.&lt;br /&gt;
在我的目标设备, 运行前面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustup target list&lt;/code&gt; 命令后, 显示&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;armv7-unknown-linux-gnueabihf (installed)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;稍微提醒一下, 这个是刚装完 Rust 后的情况, 你要是已经按下面的操作添加了各种 target, 那这个就不准了.&lt;br /&gt;
然后, 在宿主设备上, 用以下命令&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rustup target add armv7-unknown-linux-gnueabihf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;来添加对应的交叉编译目标.  直接编译试试&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo build &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt; armv7-unknown-linux-gnueabihf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;到这一步, 你会看到一大堆的错误(要是这就成功了, 那我就不写这篇文章了)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: linking with `cc` failed: exit status: 1
  |
  = note: &quot;cc&quot;
= note: clang: warning: argument unused during compilation: &apos;-pie&apos; [-Wunused-command-line-argument]
          ld: unknown option: --as-needed
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;基本上的意思, 就是虽然编译是好了, 但是链接的时候发生错误了, 原因在于现在明显是使用了宿主的链接器(linker), 而不是对应的目标设备的链接器.&lt;/p&gt;

&lt;p&gt;这里有几种解决办法&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;手动下载目标的链接器, 参考&lt;a href=&quot;https://john-millikin.com/notes-on-cross-compiling-rust&quot;&gt;这篇&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;直接用 brew 安装一下链接器 (不一定所有 target 都有).  参考&lt;a href=&quot;https://sigmaris.info/blog/2019/02/cross-compiling-rust-on-mac-os-for-an-arm-linux-router/&quot;&gt;这篇&lt;/a&gt;,&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我这里是 ARM 设备, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; 是有的, 为了简单, 我直接用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; 命令安装了&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew install arm-linux-gnueabihf-binutils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后, 在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.cargo/config&lt;/code&gt; 中, 添加如下两行配置, 修改对应 target 的链接器设置&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[target.armv7-unknown-linux-gnueabihf]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;linker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;arm-linux-gnueabihf-ld&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;再用前面的命令编译试试, 此时还是会报错:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  = note: arm-linux-gnueabihf-ld: cannot find -lgcc_s: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -lutil: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -lrt: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -lpthread: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -lm: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -ldl: No such file or directory
          arm-linux-gnueabihf-ld: cannot find -lc: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;简单的原因, 就是一切就绪了, 但是对应的一些 gnu 基础库找不到. 此时可以去找到对应的库都下到本地, 还有, 我找到一个神奇的方法, 换成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;armv7-unknown-linux-musleabihf&lt;/code&gt; 这个 target, 这里用了 &lt;a href=&quot;https://zh.m.wikipedia.org/zh/Musl&quot;&gt;musl&lt;/a&gt; 这个库, 就不需要用 gnu 的那些库了.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% cargo build --target=armv7-unknown-linux-musleabihf
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译后, 你可以可以在工程的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target/armv7-unknown-linux-musleabihf&lt;/code&gt; 目录下面, 找到交叉编译后的文件. 在目标设备上实测运行, 是可以运行的.&lt;br /&gt;
最后这种方式, 是绕过了配置链接需要的库, 理论上, 手动下载回来然后配置正确的话, 可以在 Mac 上直接编译链接成功, 应该会非常麻烦.&lt;br /&gt;
当我准备使用 SDL 这种库, 一定需要配置正确链接的库时, 这种方法就不好用了.&lt;br /&gt;
接下来, 介绍一种更全面的方法.&lt;/p&gt;

&lt;h1 id=&quot;使用-cross-rs&quot;&gt;使用 cross-rs&lt;/h1&gt;
&lt;p&gt;这个方式, 也是我个人比较推荐的方式, 万能, 而且是利用 Docker 环境来编译, 不需要在本地装一大堆纯为了编译的各种库, 当然, 代价是得装 Docker, 而众所周知, Docker 的 image 动不动就几个 G 的大小.-_-! 可能好处就是不用的时候, 清理起来方便一些了. &lt;br /&gt;
而且, 使用 Docker, 可以直接基于 Ubuntu 这种 Linux 环境, apt 的包管理感觉比 Mac  的 brew 还是要更强大.&lt;br /&gt;
对了, 其实接下来的步骤, 虽然是利用了 Docker, 但是除了 Docker 的部分, 也可以看做是在 Linux 上使用 Rust 交叉编译的过程. 假如你的宿主机本身是 Linux 的话, 那这些方法也是可以直接使用的(就不用 Docker 了), 怎么说呢, 果然 Linux 才是对开发者最友好的系统.&lt;/p&gt;

&lt;h2 id=&quot;安装-cross-rs&quot;&gt;安装 cross-rs&lt;/h2&gt;
&lt;p&gt;参考 &lt;a href=&quot;https://github.com/cross-rs/cross&quot;&gt;cross-rs 的页面&lt;/a&gt;, 每一步都相对清晰.
安装 cross-rs&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo install cross
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后, 在交叉编译的时候, 直接用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross&lt;/code&gt; 命令, 替换掉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt;. 比如我们前面的那个简单例子, 在安装 cross-rs 后, 改成用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross&lt;/code&gt; 命令.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% cross build --target=armv7-unknown-linux-gnueabihf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在下载了一个 Docker image 后, 直接就成功了…有点意外加惊喜.&lt;br /&gt;
此时, 能看到多了一个用于编译 armv7-unknown-linux-gnueabihf 的 docker image.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% docker image list
rustembedded/cross   armv7-unknown-linux-gnueabihf-0.2.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;链接外部库&quot;&gt;链接外部库&lt;/h2&gt;
&lt;p&gt;这里用 SDL 为例子, 演示怎么加载外部库.&lt;br /&gt;
首先随便找个 &lt;a href=&quot;https://sunjay.dev/learn-game-dev/opening-a-window.html&quot;&gt;SDL 的例子&lt;/a&gt;.
然后继续按上面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cross&lt;/code&gt; 命令编译, 会报链接错误&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  = note: /usr/lib/gcc-cross/arm-linux-gnueabihf/5/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2
          /usr/lib/gcc-cross/arm-linux-gnueabihf/5/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_mixer
          /usr/lib/gcc-cross/arm-linux-gnueabihf/5/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_image
          /usr/lib/gcc-cross/arm-linux-gnueabihf/5/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_ttf
          /usr/lib/gcc-cross/arm-linux-gnueabihf/5/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lSDL2_gfx
          collect2: error: ld returned 1 exit status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很明显, 就是前面多次碰到的找不到对应的动态库.  不过这次我们解决这个问题.&lt;br /&gt;
前面我们已经能看到默认情况下, cross-rs 会给我们添加一个 image, 但是这个 image 里面没有我们需要的库.  我们来添加一下.&lt;/p&gt;

&lt;h3 id=&quot;1-交互式运行这个-image-创建一个-container&quot;&gt;1. 交互式运行这个 image, 创建一个 container&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% docker run -i -t rustembedded/cross:armv7-unknown-linux-gnueabihf-0.2.1 /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-在-container-中添加我们需要的库&quot;&gt;2. 在 container 中添加我们需要的库&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# apt-get update
# dpkg --add-architecture armhf
# apt-get update
# apt-get install libsdl2-dev:armhf libsdl2-mixer-dev:armhf libsdl2-ttf-dev:armhf libsdl2-image-dev:armhf libsdl2-gfx-dev:armhf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这里是以 SDL 为例, 其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dpkg --add-architecture armhf&lt;/code&gt; 的这一步, 很关键, 因为 Docker 运行的也不是目标设备的系统, 后面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install&lt;/code&gt; 的时候, 也用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:armhf&lt;/code&gt; 的后缀, 表示安装的是针对 ARM 的对应包. 要是没有这几步, 直接用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; 安装也是没有用的.&lt;/p&gt;

&lt;h3 id=&quot;3-用这个做好的-container-来创建我们自定义的-image&quot;&gt;3. 用这个做好的 container 来创建我们自定义的 image&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% docker ps
CONTAINER ID   IMAGE                                                    COMMAND       CREATED          STATUS          PORTS     NAMES
98744316d89e   rustembedded/cross:armv7-unknown-linux-gnueabihf-0.2.1   &quot;/bin/bash&quot;   19 minutes ago   Up 19 minutes             naughty_mccarthy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;此时, 注意我们正在运行的container id.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% docker commit 98744316d89e my/clockwork
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-然后指定-cross-rs-使用我们自定义的-image&quot;&gt;4. 然后指定 cross-rs 使用我们自定义的 image&lt;/h3&gt;
&lt;p&gt;在工程中, 增加一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cross.toml&lt;/code&gt; 文件, 内容如下:&lt;/p&gt;
&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[target.armv7-unknown-linux-gnueabihf]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my/clockwork&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;准备就绪, 再次使用&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% cross build --target=armv7-unknown-linux-gnueabihf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;编译, 搞定. 此时可以将编译好的程序拷贝到目标设备上运行, 没有问题.
最后, 因为本身就是配置一个编译环境, 直接交互式运行 image 还是挺方便的, 以后有更多依赖库的时候, 重复上述步骤即可.&lt;/p&gt;

&lt;h1 id=&quot;参考&quot;&gt;参考&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://rust-lang.github.io/rustup&quot;&gt;The rustup book&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/cross-rs/cross&quot;&gt;cross-rs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://john-millikin.com/notes-on-cross-compiling-rust&quot;&gt;Notes on cross-compiling Rust&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sigmaris.info/blog/2019/02/cross-compiling-rust-on-mac-os-for-an-arm-linux-router/&quot;&gt;Cross compiling Rust on Mac OS for an ARM Linux router&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sunjay.dev/learn-game-dev/intro.html&quot;&gt;Learn Game Development in Rust&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/cross-compile-with-rust.html&quot;&gt;Rust 的交叉编译&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on May 18, 2022.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[用 Dart 加 Pixijs 写 HTML 游戏]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/dart-with-pixijs.html" />
  <id>https://jtianling.com/dart-with-pixijs</id>
  <published>2020-03-09T00:00:00+08:00</published>
  <updated>2020-03-09T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近 &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; 的流行, 让 &lt;a href=&quot;https://dart.dev/&quot;&gt;Dart&lt;/a&gt; 这个似乎已经要死的语言又复活了.  最近在找能同时在 iOS 和 H5 两端同时运行的编程语言, 没想到 Dart 竟然是非常合适的对象, 有点意外, 于是看了看 Dart.&lt;/p&gt;

&lt;!-- more --&gt;
&lt;h1 id=&quot;提要&quot;&gt;提要&lt;/h1&gt;

&lt;p&gt;实在的说, 语法特性并没有什么亮点. 类型系统除了添加了类似 Mixin 这种相对靠谱的东西外, 写起来和 C++ 的感觉都差不多, 并发上, 添加了类似 JavaScript 的 Async-Await 异步写法, 暂时没有尝试, 不过就使用 JavaScript 的经验来说, 可能真用来写服务器, 并不能很好的写 多线程/多协程 的程序, 可能要用单线程-多进程的思维来实现并发.&lt;/p&gt;

&lt;h1 id=&quot;dart-with-pixijs&quot;&gt;dart with pixijs&lt;/h1&gt;
&lt;p&gt;为了熟悉语法, 把最近 &lt;a href=&quot;/learn-python-by-game-examples-2.html&quot;&gt;用 Python 写游戏&lt;/a&gt;里面的例子实现了一下. 也算有些坑, 主要是在 Dart 和 JS/Dom 的交互上的, 比如获取键盘响应等事件上.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jtianling/dart-pixi-test&quot;&gt;源代码&lt;/a&gt;放在 Github 上了, 因为是写的第一个 Dart 程序, 写的难看的地方, 就不要太在意了…&lt;/p&gt;

&lt;p&gt;因为是 H5 版本, 可以直接通过这个链接看到效果:
&lt;a href=&quot;http://www.jtianling.com/dart-pixi-test&quot;&gt;http://www.jtianling.com/dart-pixi-test&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;引用的库&quot;&gt;引用的库&lt;/h1&gt;
&lt;p&gt;pixi 的 Dart 封装:
&lt;a href=&quot;https://pub.dev/packages/pixi&quot;&gt;https://pub.dev/packages/pixi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;pixijs:
&lt;a href=&quot;https://www.pixijs.com&quot;&gt;https://www.pixijs.com&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/dart-with-pixijs.html&quot;&gt;用 Dart 加 Pixijs 写 HTML 游戏&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 09, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[用 Python 写游戏 第二篇]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/learn-python-by-game-examples-2.html" />
  <id>https://jtianling.com/learn-python-by-game-examples-2</id>
  <published>2020-03-08T00:00:00+08:00</published>
  <updated>2020-03-08T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;学编程后, 过了初期的语法熟悉阶段, 新手往往会比较迷茫, 因为也不知道编程能干嘛, 对于这种情况, 我的建议当然是实际的做一些项目, 这里用一些游戏和 Python 的例子, 来真正的了解和熟悉编程吧.&lt;/p&gt;

&lt;p&gt;本文为该系列的第二篇&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;主角就绪&quot;&gt;主角就绪&lt;/h1&gt;
&lt;p&gt;参考&lt;a href=&quot;/learn-python-by-game-examples-1.html&quot;&gt;教程的第一篇&lt;/a&gt;及&lt;a href=&quot;/learn-python-by-game-examples-1-answer.html&quot;&gt;第一篇课后的答案&lt;/a&gt;, 我们已经获得了一个操作上较为靠谱的主角, 暂时用一个小圆点来表示.
再次回顾一下代码:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;    
&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
   
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;        

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;添加敌人&quot;&gt;添加敌人&lt;/h1&gt;
&lt;p&gt;只有主角, 没有敌人, 那叫什么游戏啊, 我们用小方块来表示敌人吧, 还记得怎么绘制方块吗?&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的代码只有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 部分, 不过也能看到, 屏幕上多了一个红色, 看起来很危险的敌人.&lt;/p&gt;

&lt;h1 id=&quot;让敌人动起来&quot;&gt;让敌人动起来&lt;/h1&gt;
&lt;p&gt;敌人是静止的, 也没有什么意思, 我们先做一个能追踪主角的敌人吧. 这个时候, 几何知识不够丰富也没有关系, 我们想一想, 怎么样才能让敌人追上主角呢?
最朴素的思想是, 让敌人的 x 坐标和 y 坐标, 都尽量的向主角靠拢, 这个想法够朴素了吧, 代码如下:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;    
&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
   
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;        

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;需要注意的时候, 注意什么样的代码, 写在什么地方, 让敌人向主角靠拢的代码, 记得写在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; 函数里面, 运行以后, 你会发现, 敌人现在直奔主角而去, 几乎难逃他的魔掌. 作为游戏玩法, 因为敌人的速度和主角一样, 主角几乎难逃魔掌, 并且, 我们能发现, 随着全局变量的增加, 我们的代码有太多的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global&lt;/code&gt; 变量需要声明, 是时候简化一下代码了.&lt;/p&gt;

&lt;h1 id=&quot;引入类型&quot;&gt;引入类型&lt;/h1&gt;
&lt;p&gt;我们用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; 来优化一下代码, 首先用一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Global&lt;/code&gt; 的类型, 来简化全局变量的声明和使用, 另外引入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Player&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enmey&lt;/code&gt;, 我们先从简化 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Draw&lt;/code&gt;函数的实现开始:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;        

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;代码比原来多了一些内容, 要是还不明白 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Player&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enemy&lt;/code&gt; 几个类, 还有定义在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Game&lt;/code&gt; 类型的变量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt; 中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enemy&lt;/code&gt; 变量的含义, 建议赶紧回去复习一下 Python 的相关内容, 所谓面向对象编程, 不知道怎么定义类型和对象, 那可不行.
上面的代码, 我们的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enemy&lt;/code&gt; 对象, 自己处理了自己的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 而不是由外部来处理, 这种代码设计的方向, 我们称其为自己对自己负责, 这是一个能导向良好代码设计的方向, 因为只有所有对象都为自己负责, 才不需要把自己的更多内容, 告诉外部, 让外部控制自己. 做人也是这样, 不是吗?&lt;/p&gt;

&lt;h1 id=&quot;更负责的对象&quot;&gt;更负责的对象&lt;/h1&gt;
&lt;p&gt;既然要做一个负责任的对象, 当然不能仅仅为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 这一件事情负责, 自己的移动这么重要的事情, 当然也不能交给别人来做. 接下来, 我们让 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enmey&lt;/code&gt; 对象自己也接管自己的移动吧.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_x&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cur_speed_y&lt;/span&gt;
                                

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                                &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                                
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;chase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_speed&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种自己为自己负责, 把内部的变量(也叫属性)不让外部使用, 而是仅仅自己知道和使用的方式, 我们称之为 &lt;strong&gt;封装&lt;/strong&gt;, 按其字面意思的理解就很贴切了, 意思就是把自己封闭起来,装的很牛的样子…
上面的代码, 已经比原来的玩具代码, 更像正常该有的样子了, 类似 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_key_down&lt;/code&gt; 函数里面的内容很少, 仅仅只有对某个对象的函数的调用, 而真正的逻辑, 都写在&lt;strong&gt;类&lt;/strong&gt;里面.
上面的代码, 其实和前面刚添加完敌人时候的作用一模一样, 但是样子却变化很大, 首先可以自己感受一下, 两者的区别.&lt;/p&gt;

&lt;h1 id=&quot;课后练习&quot;&gt;课后练习&lt;/h1&gt;
&lt;p&gt;今天我们添加了敌人, 并且尝试用类封装了主角和敌人, 可能经过这么多修改, 了解了很多新的概念, 但是你还对这些复杂的操作将信将疑, 为什么我们需要这么麻烦呢? 前面的代码看起来也挺直观的, 不是也挺好的吗?
通过一个练习, 我们来感受一下这么封装的好处吧, 那就是, 我们随机的, 在屏幕上生成更多的敌人, 比如每 5 秒,添加 1 个, 上限 100 个.&lt;/p&gt;

&lt;p&gt;简单的提示, 用一个列表来存储所有的敌人吧.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/learn-python-by-game-examples-2.html&quot;&gt;用 Python 写游戏 第二篇&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 08, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[用 Python 写游戏 第一篇]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/learn-python-by-game-examples-1.html" />
  <id>https://jtianling.com/learn-python-by-game-examples-1</id>
  <published>2020-03-07T00:00:00+08:00</published>
  <updated>2020-03-07T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;学编程后, 过了初期的语法熟悉阶段, 新手往往会比较迷茫, 因为也不知道编程能干嘛, 对于这种情况, 我的建议当然是实际的做一些项目, 这里用一些游戏和 Python 的例子, 来真正的了解和熟悉编程吧.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;前提&quot;&gt;前提&lt;/h1&gt;
&lt;p&gt;以下的教程基于 Python3.7, 并且假设你已经会 Python 的基本语法和概念了.&lt;br /&gt;
要是还没有学会, Python 的各种教程太多了, 先找来学习一下吧.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.7/tutorial/index.html&quot;&gt;官方文档&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.liaoxuefeng.com/wiki/1016959663602400&quot;&gt;廖雪峰的 Python教程&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;用什么写游戏&quot;&gt;用什么写游戏&lt;/h1&gt;
&lt;p&gt;写游戏一般都会用到一个叫&lt;strong&gt;游戏引擎&lt;/strong&gt;的库, 游戏引擎的概念来自汽车引擎, 汽车引擎驱动汽车, 提供动力, 游戏引擎驱动游戏, 简化我们写游戏的步骤.&lt;/p&gt;

&lt;p&gt;这里, 为了最友好的面向初学者, 我们首先用的是一个叫 pyzero 的游戏引擎, 这个引擎本身就是设计给初学者使用的, 所以接口和使用方式非常简单.&lt;/p&gt;

&lt;h1 id=&quot;准备环境&quot;&gt;准备环境&lt;/h1&gt;

&lt;p&gt;准备一个专业的环境, 一共分 3 步:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;安装 Python&lt;/li&gt;
  &lt;li&gt;安装 pyzero 库&lt;/li&gt;
  &lt;li&gt;找个编辑器, 比如 VSCode&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;想长久享受编程乐趣的话, 可以自己学习一下怎么弄, 环境都没弄好, 估计 Python 也还没学会. 不过对于初学者, 还是有个简化的选项, 那就是 &lt;a href=&quot;https://codewith.mu/&quot;&gt;&lt;strong&gt;Mu&lt;/strong&gt;&lt;/a&gt;, 下载, 安装, bong! 以上 3 步的工具, 就都有了.&lt;/p&gt;

&lt;p&gt;不得不感叹, 现在的编程世界, 对初学者的大门是完全敞开的, 学不学得会, 只取决于想不想学, 其实没有什么门槛.&lt;/p&gt;

&lt;p&gt;下面的教程, 都基于 Mu 来说明.&lt;/p&gt;

&lt;h1 id=&quot;mu-的操作&quot;&gt;Mu 的操作&lt;/h1&gt;
&lt;p&gt;Mu 上面一排大大的按钮, 选择 Mode, 在列表里面选择 Pygame Zero, 就表示我们准备用 pyzero 来写游戏了.
写完游戏, 按 Save 保存, 方便下次用 Load 选择文件(也可以直接把文件拖到编辑区域) 继续编写.
编写完后, 按 Play 运行游戏.&lt;/p&gt;

&lt;h1 id=&quot;第一个可运行的程序&quot;&gt;第一个可运行的程序&lt;/h1&gt;
&lt;p&gt;在编程里, 我们一般把一个方向, 最小规模的一个程序, 叫做 &lt;strong&gt;Hello World&lt;/strong&gt; 程序, 这个惯例来自于一本上世纪特别流行的 C 语言教材, 有兴趣的朋友可以去了解一下.&lt;/p&gt;

&lt;p&gt;下面看我们的程序:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;就这么夸张, 就两行代码, 一个函数…
简单说明一下, 定义一个叫 &lt;strong&gt;draw&lt;/strong&gt; 的函数, 然后用 &lt;strong&gt;screen&lt;/strong&gt; 对象里面的 &lt;strong&gt;fill&lt;/strong&gt; 函数表示背景的填充.&lt;br /&gt;
运行上面的代码, 你会得到一个夸张的红背景.&lt;/p&gt;

&lt;p&gt;其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 函数, 我们并没有像普通程序一样, 由我们自己调用, 而是由 pyzero 游戏引擎在恰当的时候调用, 我们一般把这种函数, 叫做 &lt;strong&gt;回调函数&lt;/strong&gt;, 表示我们不直接调用, 也不关心具体什么时候这个函数被调用, 只提供实现, 而是由类似 pyzero 这样的游戏引擎或者框架调用.
当然, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 函数, 就像它的英文意思一样, 表示需要在屏幕上画东西的时候被调用, 我们的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen.fill((128, 0, 0))&lt;/code&gt; 表示我们具体想画的是什么东西.&lt;br /&gt;
而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen&lt;/code&gt; 对象, 属于 pyzero 游戏引擎帮我们实现的对象, 用来简化我们的绘制步骤, 下面我们会用到很多.&lt;/p&gt;

&lt;h1 id=&quot;简单解释一下颜色&quot;&gt;简单解释一下颜色&lt;/h1&gt;
&lt;p&gt;为什么上面 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(128, 0, 0)&lt;/code&gt; 表示红色? 首先, 颜色的表示, 我们一般用所谓的光学三原色来表示, 也就是熟称的 红(Red), 绿(Green), 蓝(Blue), 我们往往用三个单词的第一个字母缩写, 即 RGB 来表示颜色的值, RGB 每个数值的范围一般是 0 到 255.&lt;br /&gt;
上面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(128, 0, 0)&lt;/code&gt; 是 Python 里面的 &lt;a href=&quot;https://docs.python.org/zh-cn/3.7/tutorial/datastructures.html#tuples-and-sequences&quot;&gt;元组&lt;/a&gt;, 三个整数分别表示我们需要的颜色的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(R, G, B)&lt;/code&gt; 值. 
我们可以尝试调整这个元组数值, 看看打开窗口颜色的变化. 比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0, 255, 0)&lt;/code&gt; 表示纯绿色.&lt;/p&gt;

&lt;h1 id=&quot;画一些其他的东西&quot;&gt;画一些其他的东西&lt;/h1&gt;
&lt;p&gt;比如, 我们先画一个白色的圆, 来表示主角.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行程序, 能看到在漆黑像夜空一样的背景中, 有一个想星星一样的圆点.&lt;/p&gt;

&lt;p&gt;这个圆点还是用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen&lt;/code&gt; 对象来实现绘制, 现在我们用的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen&lt;/code&gt; 对象里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 对象的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circle&lt;/code&gt; 函数(中文表示填满的圆的意思).
函数的具体参数的含义, 可以参考&lt;a href=&quot;https://pygame-zero.readthedocs.io/en/stable/builtins.html#screen&quot;&gt;pyzero官网&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Draw the outline of a circle.
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数解释:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;第一个参数, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(160, 120)&lt;/code&gt; 也是一个元组, 表示我们想画的这个圆点的位置.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt; 表示我们想画的圆的半径, 也就是用来表示圆有多大&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(255, 255, 255)&lt;/code&gt;, 要是前面的内容看的认真, 看到这里就知道了, 这个表示圆的颜色&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;知道了参数类型以后, 我们尝试改改这几个参数, 看看画出来的圆的效果变化情况吧.&lt;/p&gt;

&lt;h1 id=&quot;坐标的含义&quot;&gt;坐标的含义&lt;/h1&gt;

&lt;p&gt;位置坐标的含义, pyzero 里面用的位置, 使用的是所谓的屏幕坐标系, 以左上角为原点(即 0,0 点), X 向右增加, Y 向下增加.
&lt;img src=&quot;/public/images/2020/axis.png&quot; alt=&quot;坐标示意图&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;更多形状&quot;&gt;更多形状&lt;/h1&gt;

&lt;p&gt;参考前面的&lt;a href=&quot;https://pygame-zero.readthedocs.io/en/stable/builtins.html#screen&quot;&gt;pyzero官网&lt;/a&gt;, 只要你看明白了参数的类型, 试试更多的形状吧.&lt;/p&gt;

&lt;p&gt;我推荐试试下面几个常用的, 下面是参数说明&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Draw a line from start to end. 画线
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw the outline of a circle.
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw a filled circle. 实心圆
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw the outline of a rectangle. 矩形
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw a filled rectangle. 实心矩形
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Draw text. 文字
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;试试运行下面这个程序, 看看各种形状&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Draw a line from start to end.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Draw the outline of a circle.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Draw a filled circle. 
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Draw the outline of a rectangle.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Draw a filled rectangle. 
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;动画&quot;&gt;动画&lt;/h1&gt;
&lt;p&gt;我们都知道, 游戏不是静态的图片, 那么我们光靠静态的绘制, 也没有什么意思, 现在我们尝试让前面绘制的圆动起来.&lt;/p&gt;

&lt;p&gt;看看下面这个程序:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先运行一下上面的程序, 能看到一个圆, 一直在向右移动.&lt;/p&gt;

&lt;h2 id=&quot;位置变量的定义&quot;&gt;位置变量的定义&lt;/h2&gt;
&lt;p&gt;我们在新的程序里面, 不再直接用位置坐标来绘制圆了, 我们用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player_pos_x&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;player_pos_y&lt;/code&gt; 两个变量, 分别表示圆的 x 坐标位置和 y 坐标位置.&lt;/p&gt;

&lt;h2 id=&quot;update-回调函数&quot;&gt;update 回调函数&lt;/h2&gt;
&lt;p&gt;这个程序已经比前面复杂很多了, 特别是我们多了一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; 函数, 就像 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 函数一样, 这个函数也是一个回调函数, 一般我们将我们想做的游戏逻辑, 写在这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; 函数里面.&lt;br /&gt;
在这个例子里面, 我们是修改了 x 坐标的位置.
你自己尝试一下, 让圆从现在的从左往右移动, 改成从右往左试试. 
还有, 可以尝试让圆在 y 轴上移动试试.&lt;/p&gt;

&lt;p&gt;需要稍微注意的一点是, 在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; 函数里面, 因为需要修改函数外部的全局变量, 需要用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global&lt;/code&gt; 关键字说明, 不然 Python 里面默认是找局部变量的.&lt;/p&gt;

&lt;h2 id=&quot;清理屏幕&quot;&gt;清理屏幕&lt;/h2&gt;
&lt;p&gt;上面还有一个小的地方需要注意, 和原来的直接绘制圆不同, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; 函数里面在画圆之前, 多了一句 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen.clear&lt;/code&gt;. 表示在每次绘制的时候, 先把屏幕清理干净.
为了加深对清理屏幕作用的理解, 你可以尝试把这一行删掉, 看看会发生什么.&lt;/p&gt;

&lt;h1 id=&quot;操作&quot;&gt;操作&lt;/h1&gt;
&lt;p&gt;游戏, 也不光光是动画, 我们还要能操作才行.  说到这里, 我已经能给出游戏的完整表述的:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;游戏, 就是可以即时交互的动画&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;试试下面的程序, 并在程序运行时, 尝试按按键盘或者任何可以操作键.&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; 
    
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们能看到, 除了圆持续的还是往右移动以外, 每次按下任意一个按键, 圆都会往下移动 10 个像素.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_key_down&lt;/code&gt; 这个回调函数, 会在我们按下按键的时候产生被调用. 不过目前我们并没有判断按下的是什么按键.&lt;/p&gt;

&lt;h1 id=&quot;真实的操作&quot;&gt;真实的操作&lt;/h1&gt;

&lt;p&gt;前面的例子里面, 我们没有判断按下的按键是什么, 这个比较无聊, 我们来看看真正的简单操作应该是什么样的.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们去掉了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; 函数, 并且判断了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_key_down&lt;/code&gt; 被回调时, 传进来的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; 参数的值, 然后再按我们设定的规则去操作了这个圆, 试试吧.
具体的按键枚举, 还是可以在&lt;a href=&quot;https://pygame-zero.readthedocs.io/en/stable/hooks.html#buttons-and-keys&quot;&gt;pyzero官网&lt;/a&gt;看到&lt;/p&gt;

&lt;h1 id=&quot;课后练习&quot;&gt;课后练习&lt;/h1&gt;
&lt;p&gt;这里我留下一个思考题, 我们平时操作游戏的时候, 不会一直不停的按键盘, 那样太累, 怎么样把上面的程序改成当我们按下方向键的时候, 圆一直移动呢?&lt;/p&gt;

&lt;p&gt;这里给个提示, 你还需要实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_key_up&lt;/code&gt; 函数, 来获得我们松开键盘按键的信息.&lt;/p&gt;

&lt;h1 id=&quot;第一篇的结束&quot;&gt;第一篇的结束&lt;/h1&gt;
&lt;p&gt;我们现在已经了解了一个游戏需要的基础知识, 游戏开发的大门, 已经向我们打开了, 有想好要做什么游戏了吗?&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/learn-python-by-game-examples-1.html&quot;&gt;用 Python 写游戏 第一篇&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 07, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[用 Python 写游戏 第一篇 课后习题答案]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/learn-python-by-game-examples-1-answer.html" />
  <id>https://jtianling.com/learn-python-by-game-examples-1-answer</id>
  <published>2020-03-07T00:00:00+08:00</published>
  <updated>2020-03-07T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;用 Python 写游戏 第一篇 课后习题答案&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;第一篇课后练习&quot;&gt;第一篇课后练习&lt;/h1&gt;
&lt;p&gt;这里我留下一个思考题, 我们平时操作游戏的时候, 不会一直不停的按键盘, 那样太累, 怎么样把上面的程序改成当我们按下方向键的时候, 圆一直移动呢?&lt;/p&gt;

&lt;p&gt;这里给个提示, 你还需要实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_key_up&lt;/code&gt; 函数, 来获得我们松开键盘按键的信息.&lt;/p&gt;

&lt;h1 id=&quot;答案&quot;&gt;答案&lt;/h1&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filled_circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;    
&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
   
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;        

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;player_pos_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_key_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;该习题完成后, 我们就有了一个可以较自由操作的主角了, 期待接下来的课程吧.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/learn-python-by-game-examples-1-answer.html&quot;&gt;用 Python 写游戏 第一篇 课后习题答案&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on March 07, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[读瑞 达利欧的"原则"]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/review-of-principles.html" />
  <id>https://jtianling.com/review-of-principles</id>
  <published>2018-12-16T00:00:00+08:00</published>
  <updated>2018-12-16T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;作者瑞·达利欧(Ray Dalio), 是世界上当前资产规模最大的对冲基金, 桥水基金的创始人.  听着 “原则” 这种书名, 又是所谓 “成功人士” 写的书, 放在两年前的话, 这种书我光看介绍, 都会直接过滤掉, 无非就是那种 “成功人士” 向你兜售一些所谓的成功秘方, 只要按照”我”的做, 你就能像”我”一样成功这种.&lt;br /&gt;
但是, 去年我因为别人推荐, 看了查理芒格的 “穷查理宝典”, 对做投资的人有了一些新的认识, 甚至对这个世界也有了一些新的认识.  总的来说, 就是我发现, 要是一个人纯粹靠投资就能成功, 一定是对这个世界的运行规律, 有非常深刻的认识, 而为了对这个世界有充分的认识呢, 这个人读的书一定非常多, 比如查理芒格, 你看他的书, 会感觉他就不像个做投资的, 更像个学者, 或者哲学家那种, 这个以后有机会再说说.&lt;br /&gt;
实际上, 看完 “原则” 这本书, 也会觉得 瑞·达利欧 是一个很理想主义的学者.&lt;br /&gt;
原则这本书分三部分, 第一部分是作者的自传, 第二, 三部分分别是生活原则和工作原则, 原则有好几百条, 我只选择感触最深的三个讲一下.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;作者的经历&quot;&gt;作者的经历&lt;/h1&gt;
&lt;p&gt;第一部分的故事非常有意思, 瑞·达利欧讲述了他自己怎么一次又一次的犯下很严重的错误, 好像每一次都快破产了, 然后每次犯错后是怎么思考的, 最后他得出来了一个很有意思的思维过程, 那就是从认为 “我是对的”, 变成问自己 “我怎么知道我是对的”, 而回答这个问题的方式, 他想的是跟其他人交流, 但是呢, 不是去交流其他人的意见或结论, 而是去理解其他人的推理过程, 并且还让别人对自己的推理过程进行 “压力测试”.  他说, “我们都可以通过这种方式降低自己犯错的可能性”.&lt;br /&gt;
我想下一次我要做一些特别重要的决定的时候, 可能真会试试这个办法.  同时, 现在我自己有一些想法的时候, 也愿意把推理的过程讲出来, 这样要是有问题, 别人也能指出来.&lt;br /&gt;
还有个故事, 我印象挺深的, 他说到他因为精力原因, 放弃了 “桥水中国合作伙伴” 这个公司的时候, 想法很有意思, 他明明确信中国会在 21 世纪成为全球最大的经济体, 而且他要是把全部精力投入到中国这边的公司的话, 他会取得很大的成功, 但是, 尽管错过了这个好机会, 但他说他并不后悔他的决定, 他说&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果你以勤奋和有创造性的方式工作, 你几乎可以得到你想要的任何东西, 但你不可能同时得到所有东西.&lt;br /&gt;
成熟意味着你可以放弃一些好的选择, 从而追求更好的选择.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;他说的意思首先是说不要贪心, 而且, 成熟的选择, 意味着放弃, 我觉得比一般人看问题, 更深刻的地方在, 甚至放弃的还不是差的选择, 那没什么好纠结的, 为了更好的选择, 我们连一些很好的选择都应该放弃.&lt;/p&gt;

&lt;h1 id=&quot;真实的面对自己--极度求真和极度透明&quot;&gt;真实的面对自己 + 极度求真和极度透明&lt;/h1&gt;
&lt;p&gt;然后是我感触最深的两个原则.&lt;br /&gt;
讲生活原则的时候, 瑞·达利欧说要真实的面对自己, 讲工作原则的时候, 瑞·达利欧说要极度求真和极度透明.&lt;br /&gt;
我觉得这两个事情本质上是类似的, 一个是真实的面对自己, 一个是真实的对待他人.&lt;/p&gt;

&lt;h2 id=&quot;先说真实的面对自己&quot;&gt;先说真实的面对自己&lt;/h2&gt;
&lt;p&gt;首先, 真实的面对自己真的很难, 因为人的本性就是会拔高自己的, 比如瑞·达利欧就举了个例子, 假如让大家评估自己在某个机构中的贡献比例, 得到的总数大概是 300%, 也就是平均每人会高估自己 3 倍.&lt;br /&gt;
其次, 人是不会愿意暴露自己缺点的, 往往要隐藏起来, 这样在别人的心目中的形象会更好一些.&lt;br /&gt;
然后, 人都愿意听好话, 不会愿意承认自己实际是有缺点的, 听到对自己的批评本身就是有很强的挫折感, 很痛苦.&lt;br /&gt;
那要对自己真实, 怎么做到呢:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不要为自身形象担心, 只需关心能不能实现你的目标.&lt;/li&gt;
  &lt;li&gt;不要让痛苦妨碍进步, 要理解对自己不真实本身, 实际是会阻碍自己进步的, 你都觉得自己啥都知道了, 那还学啥, 我们的目标应该是要真的成为一个很厉害的人, 而不是装成一个很厉害的人.&lt;/li&gt;
  &lt;li&gt;自我归因, 不要把不好的结果归咎于任何人, 从自己身上找原因.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;真实的面对他人&quot;&gt;真实的面对他人&lt;/h2&gt;

&lt;p&gt;瑞·达利欧说的极度透明到什么地步呢, 他说的是:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;若不想当面议论别人, 背地里也不要说, 要批评别别人就当面指出来&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这一条, 对于下属员工也适用的话, 就需要非常透明和真实才行, 还得考虑下属能否真能接受.  就算在美国, 我看瑞·达利欧自己书中也说有媒体说他疯了, 而且看到一些文章说, 桥水的新员工离职率比较高.&lt;/p&gt;

&lt;h1 id=&quot;培养人&quot;&gt;培养人&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;不断的提供反馈, 这也是我对培养人感觉最有用的手段&lt;/li&gt;
  &lt;li&gt;反馈包括鼓励和批评, 需要准确的评价&lt;/li&gt;
  &lt;li&gt;授人以渔&lt;/li&gt;
  &lt;li&gt;允许犯错, 瑞·达利欧说 他对别人宽容到什么程度时, 说:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;我可以容忍你把车蹭掉了漆或撞凹了一块, 但我不会冒很大风险让你把车子给毁了.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;怎么把被培养的人犯的错误控制在能接受的范围内, 本身也是 leader 的职责.&lt;/p&gt;

&lt;h1 id=&quot;还有很多值得一提的原则&quot;&gt;还有很多值得一提的原则&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;当心 “温水煮青蛙综合症”, 人们都有慢慢习惯于不可接受事物的倾向.  在公司看到不少这种情况, 我后来了解到这种行为是有心理学基础的, 参看 &lt;a href=&quot;https://wiki.mbalib.com/wiki/%E4%B9%A0%E5%BE%97%E6%80%A7%E6%97%A0%E5%8A%A9&quot;&gt;“习得性无助”&lt;/a&gt;, 原来看到乔布斯的 “Stay hungry, Stay foolish”, 欣赏的是用hungry和foolish来表达自己求知的状态, 现在其实发现, 最有力的字是 “Stay”, 一时hungry/foolish易, “stay”最难.&lt;/li&gt;
  &lt;li&gt;把公司当做一台运转的机器, 由”文化和人”两个部件构成, 当出现问题是, 诊断问题的步骤:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;结果是好是坏?&lt;/li&gt;
    &lt;li&gt;谁对结果负责?&lt;/li&gt;
    &lt;li&gt;如果结果不好, 是因为责任人能力不够还是机器设计有问题?&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;并且要探寻到问题的根源, 而不是表面.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;问责过程要触及你直接下属的下一级&lt;/li&gt;
  &lt;li&gt;创意择优的决策方式&lt;/li&gt;
  &lt;li&gt;几个公式:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;梦想 + 现实 + 决心 = 成功的生活&lt;br /&gt;
痛苦 + 反思 = 进步&lt;br /&gt;
思考 -&amp;gt; 原则 -&amp;gt; 算法 -&amp;gt; 好决策&lt;br /&gt;
创意择优 = 极度求真 + 极度透明 + 可信度加权的决策&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好内容太多, 无法一一列举.&lt;/p&gt;

&lt;h1 id=&quot;结束&quot;&gt;结束&lt;/h1&gt;
&lt;p&gt;用瑞·达利欧在第一章前就写到的这句话结尾:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;时间就像一条河流, 带着我们顺流而下, 我们会碰到各种问题, 需要我们去决策, 但我们没有办法停下来, 也没有办法躲避, 只能用我们选择的最好的方式去应对.&lt;/p&gt;
&lt;/blockquote&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/review-of-principles.html&quot;&gt;读瑞 达利欧的"原则"&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on December 16, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[写一个自己的Golang Module(Golang1.11以后版本支持)]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/private-module-in-golang.html" />
  <id>https://jtianling.com/private-module-in-golang</id>
  <published>2018-09-28T00:00:00+08:00</published>
  <updated>2018-09-28T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;在新的Golang 1.11版本中, 官方实验性添加了一种标准的 Module 写法, 用于替代原来非常不方便的 vendor, GOPATH 那一套东西, 经过了这么长时间, 那批老顽固总算是搞清楚了一些什么. Golang 原来的包管理, 可以说是新一代的语言里面最垃圾的.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;一个-module-的诞生-go-module-的-hello-world&quot;&gt;一个 Module 的诞生, go module 的 hello world&lt;/h1&gt;

&lt;p&gt;首先, 在 GOPATH 目录以外, 建一个 Module 想放的目录, 是的, 不要放在原来的 GOPATH 里面.&lt;br /&gt;
通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go mod init MODULE_NAME&lt;/code&gt; 来初始化一个想要建立的 Module, 比如下面这样:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go mod init github.com/jtianling/goModule

go: creating new go.mod: module github.com/jtianling/goModule
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此时会在当前目录下面新建一个叫 go.mod 的文件, 你可以理解成类似 package.json 的文件(事实上格式也挺像的)&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$cat&lt;/span&gt; go.mod

module github.com/jtianling/goModule
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后开始创建自己 Module 的内容吧&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goModule&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello, world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;提交项目文件到 github 以后, 我们找个工程来应用这个新写的 Module.&lt;/p&gt;

&lt;p&gt;新建一个 goModuleTest 目录, 用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go mod init goModuleTest&lt;/code&gt; 再初始化这个测试工程.&lt;br /&gt;
创建一个调用前面 module 的 main.go 文件:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;github.com/jtianling/goModule&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;goModule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go build&lt;/code&gt; 尝试编译运行&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go build

go: finding github.com/jtianling/goModule latest
go: downloading github.com/jtianling/goModule v0.0.0-20180928104915-d4c10f8ba563

&lt;span class=&quot;nv&quot;&gt;$.&lt;/span&gt;/goModuleTest

hello, world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行没有毛病, 而且自动化的下载了我们引用了的库.&lt;/p&gt;

&lt;h1 id=&quot;版本控制&quot;&gt;版本控制&lt;/h1&gt;
&lt;p&gt;go module 使用了&lt;a href=&quot;https://semver.org/&quot;&gt;语义化的版本号&lt;/a&gt;, 简单的说就是v(major).(minor).(patch), 实践中, 只要 API 不兼容的时候, 就应该提 major 版本, 增加 API 或者优化, 可以自增加 minor 版本号.&lt;br /&gt;
在 goModule 目录下,&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git tag v0.0.1
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git push &lt;span class=&quot;nt&quot;&gt;--tags&lt;/span&gt;

Total 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
To github.com:jtianling/goModule.git
 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;new tag]         v0.0.1 -&amp;gt; v0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后删掉 goMoudleTest 工程, 再来一次, 结果会有些不一样:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go build

go build
go: finding github.com/jtianling/goModule v0.0.1
go: downloading github.com/jtianling/goModule v0.0.1

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;go.mod

module goModuleTest

require github.com/jtianling/goModule v0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没错, 增加了版本号, 我们试试升级自己的 module, 并 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git tag v0.0.2&lt;/code&gt; 试试, 
然后在 goModuleTest 工程中, 可以通过传统的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt; 加指定的版本号升级:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ go get github.com/jtianling/goModule@v0.0.2

go: finding github.com/jtianling/goModule v0.0.2
go: downloading github.com/jtianling/goModule v0.0.2

$ cat go.mod
module goModuleTest

require github.com/jtianling/goModule v0.0.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到 go.mod 里面的内容也自动变了. 此时再 build 然后运行, 会发现 module 正常更新了.&lt;br /&gt;
假如不想指定版本, 仅仅想更新到最新版本, 可以通过&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get github.com/jtianling/goModule@latest&lt;/code&gt; 的形式更新到最新版&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get -u&lt;/code&gt; 的形式升级所有的依赖库&lt;/p&gt;

&lt;h1 id=&quot;多-package-的-module&quot;&gt;多 package 的 module&lt;/h1&gt;
&lt;p&gt;一个 package 的 module 好说, 假如你的 module 包含多个 package, 那么所有的 package 指定的时候, 直接用 package 明就好, 但是 import 的时候应该有共同的前缀.
如下:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// content.go in goModule/content&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello, world v0.0.3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// module.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goModule&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;github.com/jtianling/goModule/content&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;module-测试&quot;&gt;module 测试&lt;/h1&gt;
&lt;p&gt;module 文件同样支持 golang 自带的 unit test 方法, 可以方便的在发布前进行单元测试:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// goModule_test.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goModule_test&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;testing&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;github.com/jtianling/goModule&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestPrint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;goModule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;测试方法也一样&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go &lt;span class=&quot;nb&quot;&gt;test

&lt;/span&gt;hello, world v0.0.3
PASS
ok  	github.com/jtianling/goModule	0.005s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;私有的-module&quot;&gt;私有的 module&lt;/h1&gt;
&lt;p&gt;对于公开的 module, 按上面的做法已经够用了, 私有的 module 需要对 module 的引用做一些处理, 因为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt; 实际是利用了git, 所以我们通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git config&lt;/code&gt; 改改 url 就能做到.  下面以 github 为例.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; url.&lt;span class=&quot;s2&quot;&gt;&quot;git@github.com:&quot;&lt;/span&gt;.insteadOf &lt;span class=&quot;s2&quot;&gt;&quot;https://github.com/&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于自己的私有仓库, 可能还需要用 http:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; url.&lt;span class=&quot;s2&quot;&gt;&quot;git@github.com:&quot;&lt;/span&gt;.insteadOf &lt;span class=&quot;s2&quot;&gt;&quot;http://github.com/&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;参考&quot;&gt;参考&lt;/h1&gt;

&lt;p&gt;1.&lt;a href=&quot;https://github.com/golang/go/wiki/Modules&quot;&gt;golang wiki&lt;/a&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/private-module-in-golang.html&quot;&gt;写一个自己的Golang Module(Golang1.11以后版本支持)&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on September 28, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[读"面向模式的软件架构1-模式系统"]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/review-of-a-system-of-patterns.html" />
  <id>https://jtianling.com/review-of-a-system-of-patterns</id>
  <published>2018-02-28T00:00:00+08:00</published>
  <updated>2018-02-28T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;该书把软件的设计模式分类分的更细, 在 GOF 设计模式上, 增加了一个架构模式, 在下面增加了一个 “成例”(Idiom), 也叫代码模式.  &lt;br /&gt;
书中也算是理清了一些概念, 并给出了一些概念的定义, 但是整体看下来, 并不如 GOF 的设计模式那么经典, 特别是模式的选择上, 要么是一个分类只提供1个模式, 要么是我感觉一些所谓的模式根本不足以支撑这个分类, 还有的模式横跨了几个分类…
另外, 看了这本书后远不如看GOF的书后那种大呼过瘾的感觉, 而是感觉世间的设计模式只有一种–增加中间层&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/2018/mindmap-of-a-system-of-patterns.png&quot; alt=&quot;面向模式的软件架构1-模式系统一书的思维导图&quot; /&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/review-of-a-system-of-patterns.html&quot;&gt;读"面向模式的软件架构1-模式系统"&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on February 28, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[读"启动大脑"]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/review-of-use-your-head.html" />
  <id>https://jtianling.com/review-of-use-your-head</id>
  <published>2018-02-26T00:00:00+08:00</published>
  <updated>2018-02-26T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;用一天的上下班路上的时间把 “启动大脑” 一书快速略读了一遍, 大概花了1个多小时, 用的方法就是书上介绍的, 先看目录, 找到目标, 然后再略读一遍, 然后再回头复习一遍, 顺便画了下面的思维导图.&lt;br /&gt;
最近一年听了 “得到” 上不少的书籍音频, 但是感觉假如能自己快速的阅读的话, 还是需要自己读才能有比较深刻的印象, 不然光凭听一遍, 基本上是过耳忘.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/2018/mindmap-of-use-your-head.png&quot; alt=&quot;启动大脑一书的思维导图&quot; /&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/review-of-use-your-head.html&quot;&gt;读"启动大脑"&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on February 26, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[读"思维导图"]]></title>
 <link rel="alternate" type="text/html" href="https://jtianling.com/review-of-the-mind-map-book.html" />
  <id>https://jtianling.com/review-of-the-mind-map-book</id>
  <published>2018-02-26T00:00:00+08:00</published>
  <updated>2018-02-26T00:00:00+08:00</updated>
  <author>
    <name></name>
    <uri>https://jtianling.com</uri>
    <email></email>
  </author>
  <content type="html">&lt;p&gt;最近再次简单的略读了一下”思维导图”这本书, 感觉还是值得看一下的, 毕竟名气那么大, 至于怎么用, 那就看自己了. 顺便, 为”思维导图” 一书画了个思维导图.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/2018/mindmap-of-the-mind-map-book.png&quot; alt=&quot;思维导图一书的思维导图&quot; /&gt;&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;https://jtianling.com/review-of-the-mind-map-book.html&quot;&gt;读"思维导图"&lt;/a&gt; was originally published by  at &lt;a href=&quot;https://jtianling.com&quot;&gt;九天雁翎的博客&lt;/a&gt; on February 26, 2018.&lt;/p&gt;</content>
</entry>

</feed>
