pico8 掌机

Last October, I made the first demoscene production after an almost two-decades-long break — a demo for PICO-8 fantasy console “PICOCHAK: Attack of Donuts.” This article is “behind the scenes” or PICOCHAK.

去年10月,我中断了将近20年之久后才制作了第一个模拟场景产品-针对PICO-8幻想游戏机“ PICOCHAK:Dough of Donuts”的演示。 本文位于“幕后”或PICOCHAK。

The moment I had learned that CAFe demoparty is going to be back in 2019, I decided to take part in it. In 1999, CAFe became the first party I had visited. I wasn’t creating PICOCHAK alone; other members of the PICOCHAK team are:

当我得知CAFe演示派对将在2019年回来时,我决定参加。 1999年,CAPe成为我访问的第一个聚会。 我并不是一个人在创造PICOCHAK的。 PICOCHAK团队的其他成员是:

  • Oleg Nikitin (n1k-o/Stardust) — music

    Oleg Nikitin(n1k-o / Stardust) —音乐

  • Alexey Golubtsov (Diver/Stardust) — graphics

    Alexey Golubtsov(Diver / Stardust) —图形

  • Damir Nasyrov (Adam Bazaroff) — Tatar culture expertDamir Nasyrov(亚当·巴扎罗夫)—塔塔尔文化专家

It was easy to come up with a concept. As CAFe is held in Kazan, the capital of Tatarstan, Oleg suggested using Tatar food references. I added that I like space-themed demos. By mixing these, we got our “Tatar food in space” idea. Chak-chak is a Tatar national sweet, so we made it a protagonist in the story, hence the name — PICOCHAK. Our demo took second place in the console demo competition.

提出一个概念很容易。 由于CAFe在Ta斯坦的首都喀山举行,奥列格建议使用Ta塔尔食品作为参考。 我补充说,我喜欢以太空为主题的演示。 通过混合这些,我们得到了“太空中的塔塔尔食品”的想法。 泽恰克(Chak-chak)是塔塔尔族(Tatar)的国民甜品,因此我们使它成为故事的主角,因此得名-PICOCHAK。 我们的演示在控制台演示竞赛中排名第二。

PICOCHAK in numbers:

PICOCHAK的数量:

  • Duration: 4 minutes, 24 seconds持续时间:4分钟24秒
  • 12 effects12种效果
  • 446 8x8 sprites (2 64х64 textures, 38 8х8 font sprites, 104x64 demo logo, 8 24x24 planet sprites, 72x64 “gas station” sprite, 8 16x16 “ship” sprites).446个8x8子画面(2个64х64纹理,38个8х8字体子画面,104x64演示徽标,8个24x24行星子画面,72x64个“加油站”子画面,8个16x16个“船”子画面)。
  • 5 3D models (569 vertices and 1120 triangles in total).5个3D模型(总共569个顶点和1120个三角形)。
  • 80 music patterns.80种音乐模式。

PICO-8 has many limitations, just like real retro hardware. With this demo, I wanted to push PICO-8 limits, because that’s what retro demoscene is about, in my opinion. I believe that I succeeded. I haven’t had so much fun while coding since the 4K intro “Malady” I made for ZX Spectrum back in 1999. Never-ending battle with platform constraints, inventing coding tricks, finding smart ways to improve the code — these were a breath of fresh air, which helped me to deal with the burnout I got at my daily job.

PICO-8具有许多局限性,就像真正的复古硬件一样。 通过这个演示,我想突破PICO-8的限制,因为我认为这就是复古演示的含义。 我相信我成功了。 自从我在1999年为ZX Spectrum制作4K入门影片“ Malady”以来,我在编码方面并没有那么开心。与平台约束的永无止境的战斗,发明编码技巧,寻找改进代码的明智方法-这真是令人叹为观止新鲜的空气,这有助于我应对日常工作中的倦怠感。

工作效果 (Working on effects)

I didn’t have the goal of inventing something new and unique. For my “demoscene comeback,” I wanted to re-create some classic demoscene effects because I missed the opportunity to do them on real retro-hardware back in time. I had already had a simple 3D engine on PICO-8, but for PICOCHAK I added some new features:

我的目标不是发明新颖独特的东西。 对于我的“退化时代复出”,我想重新创建一些经典的场景效果,因为我错过了时光倒流在真正的复古硬件上进行制作的机会。 我在PICO-8上已经有一个简单的3D引擎,但是对于PICOCHAK,我添加了一些新功能:

  • Concave (non-convex) objects support凹面(非凸面)对象支持
  • Flat shading平面阴影
  • Textures (without perspective correction)纹理(不进行透视校正)

I also re-created three classic effects: roto-zoomer, twister, and bump mapping. All other effects use sprites. As mentioned above, the demo has 446 8x8 sprites, while PICO-8 has a memory for 256 sprites only. To fit this much sprites, I used the PX9 compression algorithm by Zep.

我还重新创建了三种经典效果:旋转三体,扭曲器和凹凸贴图。 所有其他效果都使用精灵。 如上所述,该演示具有446个8x8子画面,而PICO-8仅可存储256个子画面。 为了适应这些精灵,我使用了Zep的PX9压缩算法 。

I’m not going to write about the implementation of effects, because it’s pretty easy to find all the necessary information on the Internet. This article is about the unique PICO-8 challenges I faced when creating PICOCHAK.

我将不写效果的实现,因为在Internet上找到所有必要的信息非常容易。 本文介绍了我在创建PICOCHAK时面临的独特PICO-8挑战。

场景系统-将所有效果连接在一起 (Scenario system — connecting all effects together)

To transform a bunch of effects to a demo, it needs some control system which provides synchronization with music and switches effects. I called it a “scenario system.” In PICOCHAK, it went through three iterations. Every time I had rewritten the code from scratch, I got fewer tokens and better readable code. The common idea for all three iterations was that every effect is a function, which performs some initialization procedures and then returns update and draw functions for the effect.

要将一组效果转换为演示,它需要一些控制系统,该系统可与音乐同步并切换效果。 我称它为“场景系统”。 在PICOCHAK中,它经历了三个迭代。 每次我从头开始重写代码时,我都会得到更少的令牌和更好的可读性代码。 这三个迭代的共同思想是,每个效果都是一个函数,该函数执行一些初始化过程,然后返回该效果的updatedraw函数。

If you’re unfamiliar with PICO-8: update function updates the state, and draw function performs the actual drawing on the screen. Both functions are called 30 or 60 times per second. While you can put state updates and drawing code in any of these functions, it’s better to separate them. If your draw function takes more than 1/60 or 1/30 seconds to complete, you get FPS drop, but the perceived speed of objects on the screen doesn’t change, because PICO-8 will always call the update function 30 or 60 times per seconds.

如果您不熟悉PICO-8: update功能会更新状态,而draw功能会在屏幕上执行实际的绘图。 这两个功能每秒被调用30或60次。 虽然您可以在任何这些函数中添加状态更新和绘图代码,但最好将它们分开。 如果draw功能花费的时间超过1/60或1/30秒,则FPS下降,但是屏幕上物体的感知速度不会改变,因为PICO-8始终会调用update功能30或60每秒次数。

第一次尝试:状态机 (1st attempt: state machine)

Initially, I decided to “describe” each effect as a state machine, and update functions in effects had series of if expressions: one if for each state. It worked fine, but it was hard to read and took many tokens. I could’ve stayed with this system, but I needed tokens for other effects.

最初,我决定将每个效果“描述”为状态机 ,并且效果中的update函数具有一系列if表达式:每个状态一个if 。 它工作正常,但很难阅读,并使用了许多标记。 我可以一直使用这个系统,但是我需要令牌来实现其他效果。

第二次尝试:更好的状态机 (2nd attempt: better state machine)

I was still thinking that state machines are the best for programming scenarios, so I made the better version, which took fewer tokens. The code became shorter, but still hard to read. The more effects I added, the less satisfied I was with it. I started to get lost in pieces of logic scattered here and there. I needed a better solution.

我仍在考虑状态机是编程场景的最佳选择,因此我制作了更好的版本,使用了更少的令牌。 代码变得更短,但仍然很难阅读。 我添加的效果越多,对它的满意度就越低。 我开始迷失在零散的逻辑中。 我需要一个更好的解决方案。

第三次尝试:协程 (3rd attempt: coroutines)

Coroutines for the rescue! Every effect had got a coroutine with the control code, which became linear, you can read it like an actual scenario. Now, every effect function returns 4 values:

协程进行救援! 每个效果都带有与控制代码成线性关系的协程,您可以像实际情况一样阅读它。 现在,每个效果函数都返回4个值:

  • Scenario coroutine方案协程
  • update function with common animations

    具有常见动画的update功能

  • draw function

    draw功能

  • Background color (-1 if clearing screen before draw call is not needed)

    背景颜色(如果不需要在draw调用之前清除屏幕,则为-1)

To simplify synchronization, I created four helper functions: loop_frames, wait_frames, loop_sync and wait_sync. Here’s a usage example:

为了简化同步,我创建了四个帮助函数: loop_frameswait_framesloop_syncwait_sync 。 这是一个用法示例:

while loop_frames(64) dodonuty -= 2
endmove_donuts, show_mega = true, true
wait_frames(120)
while loop_frames(210) dooffy -= 1
end
wait_sync()

What this code does:

该代码的作用:

  • Decrease donuty variable by 2 every frame (repeat for 64 frames)

    donuty变量每帧减少2(重复执行64帧)

  • Set some flags设置一些标志
  • Do nothing for 120 frames (common animations in an update function are still running)

    对120帧不执行任何操作( update功能中的常见动画仍在运行)

  • Decrease offy variable by 1 every frame (repeat for 210 frames)

    offy变量每帧减少1(重复210帧)

  • Wait for the next music synchronization point等待下一个音乐同步点

Just as I said: the control code is linear and very easy to read. You don’t see any yields because they’re all inside those helper functions.

就像我说的那样:控制代码是线性的,非常易于阅读。 您看不到任何yield因为它们都在这些辅助函数内。

The overall demo scenario is a Lua table with effect functions and music synchronization points. The music synchronization point is the number of music patterns played since the beginning of the demo. Each effect may have multiple synchronization points. Here’s a part of PICOCHAK scenario:

整个演示场景是一个带效果功能和音乐同步点的Lua表。 音乐同步点是自演示开始以来播放的音乐模式的数量。 每个效果可能具有多个同步点。 这是PICOCHAK方案的一部分:

fx_intro, 1,
fx_foodstars, 4, 6, 8,
fx_donuts, 9, 13, 15, 17, 19,
fx_donut_attack, 25, 27,
fx_sky_transition,
function() return fx_preparations(104, 0, 0) end, 29,
fx_kaleidoscope, 31,

音乐控制系统 (Music control system)

Music was another challenge for us. PICO-8 doesn’t have much space for music, so it was clear: if we want to have a four-minute-long soundtrack, we need to use some tricks. Here’s what I came up with:

音乐对我们来说是另一个挑战。 PICO-8的音乐空间不大,所以很明显:如果我们想要一个四分钟长的音轨,我们需要使用一些技巧。 这是我想出的:

Pattern loops. I change loop parameters dynamically because 64 pattern slots are not enough, the “unrolled” soundtrack would have taken over 80 pattern slots.

模式循环。 我动态更改循环参数,因为64个图案插槽不够用,“展开”的音轨将占用80个图案插槽。

Overwriting patterns. At some point after the intro, I overwrite the first 12 SFX with data stored in sprite memory. This trick gave n1k-o a bit more freedom and added variety to the music.

覆盖模式。 介绍之后的某个时候,我用存储在Sprite存储器中的数据覆盖了前12个SFX。 这个技巧给n1k-o带来了更多的自由,并增加了音乐的多样性。

Audio effects. It’s an undocumented feature of PICO-8. There are 4 per-channel audio effects: reverb, filter, distortion, and “half-speed.” Turning effects on and off during loops made repetitions less annoying.

音频效果。 这是PICO-8的未记录功能。 每声道有4种音频效果:混响,滤波器,失真和“半速”。 在循环中打开和关闭效果可以减少重复的烦恼。

I created a little “music control system,” which used a list of “audio commands.” Each command consists of 2 numbers: time and the command itself. Like in the scenario system, time is measured in the number of patterns played since the beginning of the demo. Here are some examples of commands:

我创建了一个小的“音乐控制系统”,其中使用了“音频命令”列表。 每个命令由2个数字组成:时间和命令本身。 像场景系统中一样,时间是用自演示开始以来播放的模式数量来衡量的。 以下是一些命令示例:

23, 0x0404,   -- FX: filter and distortion for channel 3
24, 0x131c.8, -- Set loop for patterns 0x13-0x1c
27, 0x0.01,   -- FX: reverb on channel 1
31, 0x0.09,   -- FX: reverb on channels 1 and 4
47, 0x0.8,    -- Overwrite patterns

n1k-o, our composer, writes a lot of music for ZX Spectrum with Vortex Tracker. To let him use one of his favorite tools, I created a converter from Vortex Tracker to PICO-8 format. ZX sound chip and PICO-8 sound system are different, so it was impossible to make a perfect converter. However, we developed some conventions which allowed him to do most of the work in Vortex Tracker and use PICO-8 music editor only for the final touches.

我们的作曲家n1k-o使用Vortex Tracker为ZX Spectrum编写了大量音乐。 为了让他使用他最喜欢的工具之一,我创建了一个从Vortex Tracker到PICO-8格式的转换器。 ZX声音芯片和PICO-8声音系统不同,因此不可能制作出完美的转换器。 但是,我们制定了一些约定,使他可以在Vortex Tracker中完成大部分工作,并仅将PICO-8音乐编辑器用于最终修饰。

优化代码大小 (Optimizing code size)

There are three limitations on the code size on PICO-8:

PICO-8的代码大小有三个限制:

  • 8192 tokens8192个代币
  • 15KB of compressed code size15KB的压缩代码大小
  • 65535 characters65535个字符

The limits are listed in the order of their importance. If you’re creating something big on PICO-8, the first limit you’re going to hit is, most likely, the token limit. If you have too many tokens, you can’t even load a cart. The compressed code size limit is more forgiving — you can load and run a cart, but you won’t be able to export it to p8.png format.

限制按其重要性顺序列出。 如果您要在PICO-8上创建一些重要内容,那么您要达到的第一个限制很可能就是令牌限制。 如果令牌太多,您甚至无法加载购物车。 压缩后的代码大小限制更为宽容-您可以加载并运行购物车,但无法将其导出为p8.png格式。

When I started optimizing the code, I also began taking notes on how many tokens I saved after each optimization. According to my records, I managed to cut over 3000 tokens! That means that not optimized code would have taken over 11k tokens!

当我开始优化代码时,我也开始记下每次优化后我保存了多少令牌。 根据我的记录,我成功削减了3000多个代币! 这意味着未优化的代码将接管11k令牌!

The most token-heavy module of the demo is the 3D engine. The first function I optimized was the matrix multiplication function. It was unrolled entirely to make it faster. However, it’s used only three times each frame and doesn’t impact performance significantly, so I rewrote it using loops. The next one was a triangle rasterizer. Initially, I used a speedy implementation by ElectricGryphon, but it is very long, so I wrote my own function. It is not perfect, but it’s much shorter and only a tiny bit slower. Another significant optimization was rewriting a sorting function (I use it for concave objects). Instead of some 3rd party implementation of a merge sort, I wrote an in-place radix sort. Not only is it shorter, but it is also faster.

该演示中最繁重的模块是3D引擎。 我优化的第一个函数是矩阵乘法函数。 它完全展开以使其更快。 但是,每个帧只使用了3次,不会显着影响性能,因此我使用循环将其重写。 下一个是三角形光栅化器。 最初,我使用ElectricGryphon的快速实现,但是它很长,所以我编写了自己的函数。 它不是完美的,但要短得多,并且速度慢一点。 另一个重要的优化是重写排序函数(我将其用于凹面对象)。 我编写了一个就地基数排序,而不是某个合并排序的第3方实现。 它不仅更短,而且更快。

All 3D models in PICOCHAK are programmatically generated, and these generators took about a thousand tokens. It was OK in the beginning, but when I started to add more effects, it became clear that it’s too much. I decided to put all model data (vertices and triangles) to sprite/map memory, and I failed: there wasn’t enough space. In the end, I chose a hybrid approach: storing vertices in the map memory and building triangles programmatically.

PICOCHAK中的所有3D模型都是以编程方式生成的,这些生成器使用了大约一千个令牌。 一开始还可以,但是当我开始添加更多效果时,很明显它太多了。 我决定将所有模型数据(顶点和三角形)放入精灵/地图内存,但我失败了:空间不足。 最后,我选择了一种混合方法:将顶点存储在地图内存中并以编程方式构建三角形。

All these are the algorithmic optimizations, but there are also some conventional techniques which you can apply to any PICO-8 code:

所有这些都是算法上的优化,但是也可以将一些常规技术应用于任何PICO-8代码:

同一行上有多个作业 (Multiple assignments on the same line)

-- Instead of this:
local a = 1
local b = 2-- Write this:
local a, b = 1, 2

It makes your code difficult to read, but it saves you one token for each variable. Be careful, such assignments are “atomic,” so the code like this won’t work:

它使您的代码难以阅读,但为每个变量节省了一个标记。 请注意,这样的分配是“原子的”,因此这样的代码将不起作用:

local obj, v = arr[1], obj.field

By the way, multiple assignments are perfect for swapping variable values without using a temporary variable:

顺便说一句,多个分配非常适合在不使用临时变量的情况下交换变量值:

x, y = y, x

将重复代码移至函数 (Move repeating code to functions)

This is a pretty obvious recommendation, but it can save you a lot of tokens. I made functions even for basic calculations like this:

这是一个很明显的建议,但是它可以为您节省很多令牌。 我为这样的基本计算制作了函数:

function ssin(t, scale, tscale)return sin(t / (tscale or 1)) * scale
end

It is important to remember that each function call costs you some CPU time. In the twister effect, I had to find a balance between using functions and unrolling them to achieve 60 FPS.

重要的是要记住,每个函数调用都会花费一些CPU时间。 在扭曲效果中,我必须在使用功能与展开功能之间取得平衡,以达到60 FPS。

使用语言功能 (Use language features)

PICO-8 has some additions to Lua syntax which save tokens:

PICO-8对Lua语法进行了一些补充,以节省令牌:

a = a + 1 -- 5 tokens
a += 1 -- 3 tokens

You can use all() to iterate through arrays. It takes fewer tokens, but it’s a bit slower. Another trick is to use % instead of band(x, mask) for masking if you work with positive integers.

您可以使用all()遍历数组。 它需要较少的令牌,但速度较慢。 另一个技巧是,如果使用正整数,请使用%而不是band(x, mask)进行遮罩。

将所有字符串放入单个变量 (Put all strings to a single variable)

There’s quite a lot of text in PICOCHAK. Initially, every string took 3 tokens: the string itself and a pair of coordinates. To save tokens, I used the fact that a string of any length is just one token for PICO-8. The demo is linear, and each line is used only once, so I could merge all texts and coordinates into a single very long string variable. I shaved off over a hundred tokens with this trick!

PICOCHAK中有很多文字。 最初,每个字符串带有3个标记:字符串本身和一对坐标。 为了保存令牌,我使用了一个事实,即任何长度的字符串只是PICO-8的一个令牌。 该演示是线性的,每行仅使用一次,因此我可以将所有文本和坐标合并为一个非常长的字符串变量。 我用这个技巧刮掉了一百多个令牌!

优化压缩代码的大小 (Optimizing compressed code size)

When I was close to the completion, I finally ran into the compressed code size limit. However, it was elementary to fix:

当我接近完成时,我终于遇到了压缩代码的大小限制。 但是,修复以下内容是基本的:

  • Convert code indentation to tabs将代码缩进转换为制表符
  • Remove almost all comments删除几乎所有评论
  • Give some variables shorter names给一些变量起个简短的名字


The process of creating PICOCHAK was challenging but enjoyable. I enjoyed every minute of it, including the routine moments of code size optimization.

创建PICOCHAK的过程充满挑战,但令人愉快。 我喜欢其中的每一分钟,包括代码大小优化的例行时刻。

  • PICOCHAK at pouët.net

    在pouët.net的PICOCHAK

  • Topic at Lexaloffle BBS

    Lexaloffle BBS的主题

  • Source code on GitHub

    GitHub上的源代码

  • VT2-to-PICO-8 converter on GitHub

    GitHub上的VT2-to-PICO-8转换器

翻译自: https://habr.com/en/post/500680/

pico8 掌机

pico8 掌机_PICOCHAK的制作— PICO-8 Fantasy Console的演示相关推荐

  1. pico8 掌机_使用Pico-8构建自己的复古游戏

    pico8 掌机 An example of the kinds of pixel animations people make in Pico-8. 人们在Pico-8中制作的各种像素动画的示例. ...

  2. 制作Retropie系统树莓派掌机(三)

    制作Retropie系统树莓派掌机(三) 前面介绍了如何制作外壳和按键.这一节再聊如何组装和接线. 1.组装 安装屏幕和主板 先把屏和主板放上去,原外壳上留好限位卡扣或孔.再把电池(2节18650)和 ...

  3. 树莓派zero制作retropie掌机

    目录 总体设计 调试显示模块 调试游戏手柄模块 调试音频模块 整体组装 背景 游戏掌机对于很多人来说,都是童年时期美好的记忆,但是因为当时条件的限制,或多或少留有一些遗憾,以至于现在网络上还有很多销售 ...

  4. r4卡2020整合内核_R4卡使用方法!游戏介绍及常见问题!任天堂掌机通用

    哈喽大家好!我是掌机百科!今天介绍下R4卡的使用方法及教程,DS,NDS,NDSL,NDSLL,NDSXL,NDSI.NDSILL,NDSIXL,3DS,3DSLL,3DSXL,2DS,NEW3DS, ...

  5. 供意图转战手机平台的掌机开发者参考的10点建议

    作者:Kris Graft 独立开发工作室Vector Unit创意总监Matt Small表示,当工作室首次推出Xbox Live Arcade游戏<飓风迅雷赛艇>时,他们以为自己会一直 ...

  6. 掌机发展简史及未来趋势分析

    大致线路 Nintendo Gameboy Game boy color Game boy advance Game boy advance sp Nintendo dual screen Gameb ...

  7. gba测试软件,用手机玩GBA游戏 掌机模拟App《Delta》展开测试

    在80后和一部分90后的童年里,如果能拥有一台自己的GBA,他往往就会是小伙们当中受瞩目的焦点.不过随着科技时代的进步,这些设备已经离我们越来越远了,但好在智能手机时代的应用<GBA4iOS&g ...

  8. 《安富莱嵌入式周报》第287期:下一代Windows12界面,支持各种工业以太网协议参考,百款在线电子开发工具,seL4安全微内核,旋转拨号手机,PSP掌机逆向

    往期周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - P ...

  9. 高性价比掌机Retroid Pocket 3:搭载展锐芯片T310,采用PowerVR GPU

    Retroid Pocket 3游戏掌机厂商GoRetroid凭借其可靠的产品,极具竞争力的价格,在掌机游戏市场有着较高的影响力. 2022年9月,在Goretroid长达数月的开发和推广后,Retr ...

最新文章

  1. Windows活动目录系列---活动目录版本迁移概述
  2. 2021-06-04
  3. python各种推导式详解
  4. tftp c++ 上传_如何在 Fedora 上建立一个 TFTP 服务器
  5. LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
  6. java break的用法_Java基础break、continue语句的用法
  7. win7系统图标太大的缩小教程
  8. newman执行测试_postman+newman+Jenkins之API全自动化测试(MAC)
  9. HDOJ2013_蟠桃记
  10. Linux基本命令介绍
  11. 使用回收站主键名、索引名问题
  12. 互动教程 for Excel 2016
  13. django下载或者导出文件
  14. 计算机处理技术职业道德与论文,计算机职业道德j论文.doc
  15. 编辑视频贴纸软件_12款自媒体视频剪辑制作软件,爆款内容必备神器!
  16. MySQL数据库企业级应用实践
  17. 2022-2028年中国船舶工业行业投资潜力研究及发展趋势预测报告
  18. 33岁想从头学做网页设计_从头到头的10位客户我如何设计和推出saas产品
  19. 我啊,程序员啊程序员
  20. C#实现office文档转换为PDF格式

热门文章

  1. 删除文件时提示需要**权限, 通过命令行修改文件/目录的所有者和权限
  2. 【node】 npm install 报错:code 128
  3. 有人用苹果吗?20个出色的 Safari 插件
  4. Nginx 负载均衡服务失败场景
  5. 用python实现QQ筛人的功能
  6. FPGA烧写程序方式AS 、 PS
  7. 进一寸有进一寸的欢喜,谈谈如何优化 Milvus 数据库的向量查询功能
  8. java 父类强制转换为子类_父类强制转换为子类
  9. 学习笔记(02):前端工程师零基础到就业全套课程-表单
  10. MYSQL - 创建/更新/删除表