众所周知,怪物总是一个多动症。它们不会原地干站着:和站军姿一样。而是会因为呼吸、眨眼、位置轻微变化等而产生图片位移和变化。
好处也很明显:精致的角色像死物,而呼吸、重心位移等细微动态可以让角色更像活生生的声生命……巴拉巴拉的,剩下的不多说了,直接切入主题。
为什么这个专栏会出现,答案是:如果你想做一个原作向同人,想要抄下来原版的运动逻辑让它更有原版味道的情况下你会怎么办?诚然,你可以选择抄代码,如果你的手头没有UNDERTALE和UTMT怎么办呢?又或者,如果你拿到了代码但是看不懂而无法复刻呢?
于是,这篇专栏就出现了,它更像是一个数据汇总文章,而不是一个说明性教程。
观前提醒,本专栏包含大量代码、数学讲解和复现,如有不适请停止观看
如果你对toby丝滑的动画怎么制作的感兴趣,也可以继续阅读,但不是读代码

组成拆解:由 头部 + 身体(也可以叫腿部) 两部分组合而成。
头部:由Gamemaker Studio 1.4自带的Path系统进行画线循环控制,如图所示:

名称:path_froghead
没错,它确实是直接由Path系统完成位置移动的,我们仍然有办法复现它:
// 路径点展示
// 模式:循环模式,播放完重播
// 步长:0.5px
path_points = [
[264, 300, 100], // 点0
[272, 296, 100], // 点1
[280, 300, 100], // 点2
[272, 304, 100], // 点3
[272, 292, 100] // 点4
]; 我写了什么?
path_points是记录所有点位的一个表(或者说数组),内部所有东西都围绕[x坐标,y坐标,速度]来逐个填入。至于怎么实现点位操作,需要自己写,因为每个引擎不一样,这里只提供原版的数值参考。上方给出了步长,每帧需要移动这么多的距离,但是请注意,这是30f的情况下,现阶段绝大部分ut游戏都是60f,这个数需要除以2.
哇,好头晕啊,居然有代码,不看了不看了。
当然,再次提醒,这只是开胃小菜,如果适应不了,请及时退出!
腿部:由于站位和怪物位置有关系,这里没有固定值(我会在下面贴部分图片,来看看为什么说是不固定的),但是y值确定是136.

最上方是froggit和whimsun的配合,然后是小模怪,然后是三个小模怪,观察可以发现,没有任何规律,全都是硬编码的(说人话就是被写死的)

组成拆解:单个实例移动 构成。

名称:path_whimsun(我请问了怎么这么长)
路径点复现:
// 路径点展示
// 模式:循环模式,播放完重播
// 步长:0.5px
path_points = [
[176, 96, 100], // 点0
[176, 88, 100], // 点1
[176, 104, 100], // 点2
]; 
组成拆解:保持原地不动,只是切换眨眼动画(一共5帧图片)

组成拆解:不进行移动,只是原地的图片切换,切换样子像是随机无意识的动爪子。
下面是逻辑:
// Alarm8
image_speed = 0; // 停下动画切换
// Alarm7
image_speed = 0.5
if (mercymod < 0) // 如果还不可饶恕
{
alarm[8] = 10
// 随机30+U(0, 40)的时间再次执行这一片代码
alarm[7] = 30 + random(40)
}
else
image_speed = 0.1 // 这里是福音蟑螂可饶恕时不断蹲起
// Alarm6,这里是福音蟑螂开心时不断蹲起
if (mercymod > 0)
{
image_speed = 0.1
} 主体解析:福音蟑螂会随机30 + U(0, 40)(U指的是在这段区间内均匀随机分布的一个数)的时间执行每0.5秒切换一次图片的动画进程。同时会在十帧后重新关闭动画静止站立。上述进程会不断循环,直到切换状态为happy。

组成拆解:仅本身,不进行移动,只进行四张图片切换。

组成拆解:单个图片进行竖直方向的伸缩(image_yscale)。
if (image_yscale < 0.9)
scalevalue = 0.01
if (image_yscale > 1.1)
scalevalue = -0.01
image_yscale += scalevalue
y -= (102 * scalevalue) 如果图片伸缩小于0.9倍,那么放大倍率为0.01;如果图片伸缩大于1.1倍,那么放大倍率为-0.01(或者说缩小倍率0.01)。y值随着scalevalue的变化有y = y - 102 * sv的变化公式。
实际制作的时候可以考虑把y设置在最下方,然后只改变yscale,就没必要改变y值了。

组成拆解:它本身且无任何额外动作,除了砍击切换图片。

组成拆解:它本身进行眼睛图片切换,仅在特定操作序列之后逐帧播放戴帽子的图片。
下面是困难模式,事先声明,核心三人组在核心题目下,而不是这里,这里仅整理遗迹特有的。

组成拆解:脸(face)+ 底座(stem)+ 头上的尖锐部分(headthing)+ 两个胳膊(arm)。
公式整理如下:
底座:
x:x
y:y + 114
x伸缩公式:2 + 0.1 * sin(time / 7)
y伸缩公式:2 - 0.1 * sin(time / 7)
三个脸上的物品:
face[0]x:x + 13 * sin(time / 16)) + sin(time / 6)
face[0]y:y + 56 + 18 * cos(time / 16) + cos(time / 6)
face[0]角度:time * 3 + 4 * sin(time / 6)
face[1]和face[2]所做操作均为改变了初相,face[1]为time + 36,face[2]为time + 65
face[1]和face[2]的角度均相对于face[0]加了180
头上的尖锐部分:
x公式:x
y公式:y - 4 + 2 * sin(time / 7)
两个胳膊:
x公式:x - 12 或 x + 14
y公式:y
角度公式:-5 + 5 * sin(time / 7) 或 5 - 5 * sin(time / 7) 
组成拆解:蔬菜头部(head) + 头环(headwrap) + 五条单独的蛇(snake1~5)
公式整理:
头部:
x:x + 24
y:y + 46
头环:
x:x + 14
y:y + 24 + 2 * sin(time / 5 + 1.5)
1号蛇(最左侧开始):
x:x + 32
y:y + 52 + 2 * sin(time / 5 + 1.5)
角度:6 * sin(time / 5)
2号蛇:
x:x + 38
y:y + 46 + 1.5 * sin(time / 5 + 0.5)
角度:2 * sin(time / 5)
3号蛇(中间)
x:x + 52
y:y + 50 + 1.5 * sin(time / 5 + 1)
角度:0
4号蛇
x:x + 60
y:y + 46 + 1.5 * sin(time / 5 + 1.5)
角度:-2 * sin(time / 5)
5号蛇(最右侧)
x:x + 74
y:y + 52 + 1.5 * sin(time / 5 + 2)
角度:-6 * sin(time / 5 + 2) 
组成拆解:身体(body)(包括一整个外壳和两个缝隙)+ 腿部(leg)+ 嘴和手(mouthA/B/C)+ 长得像面罩的眼睛(eye)+ 大翅膀(wingA)+ 小翅膀(wingB)
公式整理:
左大翅膀:
x:x + 2 * sin(time / 4)
y:y + 6 + 52 + 2 * sin(time / 4)
右大翅膀y和左大翅膀一样:
x:x + 52 - 2 * sin(time / 4)
左小翅膀x和左大翅膀一样:
y:y + 16 + 56 - 2 * sin(time / 4)
右小翅膀x与大翅膀一样,y与小翅膀一样
左腿:
x:x
y:y + 100
右腿:
x:x + 50
y:y + 100
身体:
x:x
y:floor(2 * sin(time / 6)) 
组成拆解:头部(head)+ 身体(body)+ 腿部(legs)
公式整理:
腿部:
x:x
y:y
身体:
x:x
y:y + (head的y - ys) / 3
头部:
x:x
y:
if (down == 1)
{
y += (a / 2)
a += 0.05
if (a >= 1)
down = 0
}
if (down == 0)
{
y += (a / 2)
a -= 0.05
if (a <= -1)
down = 1
} 头部分段函数解释:如果要往下走,那么y每次增加a/2,a每次加0.05,如果a大于等于1,那么开始往上走;如果往上走,那么y每次减少a/2,a每次增加0.05.
总的来说,就是头部的增量是身体增量的三倍,这样制造出头比身体移动的快的感觉。

公式与雪铁龙完全一致。

组成拆解:仅本身进行图片切换。

组成拆解:身体(body)+ 眼睛和眼睛上半部分(head)+ 眼睛和眼镜下半部分(mouth)+ 云雾(cloud)+ 礼物(gift)
公式整理:
礼物:
永远保持跟随头部的x, y
身体:
保持不动
眼睛和眼睛上半部分:
如果x比初始创建的位置+8要小,那么水平速度为1,反之为-1
同时在8 + U(0, 10)时间范围内将水平速度设置为0
开始跟随嘴巴的速度移动
眼睛和眼睛下半部分:
和上半部分相反 这里说一下礼物鹿头和嘴巴的效果是什么情况:首先,礼物鹿的头分成三个节点,它会在这三个位置停下:x-8, x, x+8。其次,礼物鹿在x到x+8这段范围是嘴巴跟随头部移动的;而在x-8到x这段区域,是头部跟随嘴巴移动的。二者互相在各自范围作用,形成了在三点之间往返运动的效果。

组成拆解:身体(body)+ 头部(head)+ 双臂(arms)+ 尾巴(tail)
公式:
身体,尾巴,头部均保持不动:
手臂:
若sin(time / 4.5) > 0,那么手臂y增加0.8;
反之减少0.8 
组成拆解:mandog + womandog
这两位的图片都是循环播放的。

组成拆解:全身(lesserdog)+ 尾巴(tail)+ 头(head)
公式整理:
以防你不知道:
脖子的绘制其实是使用矩形完成的
尾巴会随着饶恕次数增加而加快图片切换速度
假设饶恕次数为mercymod / 900
头y:(mercymod - 500) / 4 + 40 
组成拆解:图片本身
公式整理:
无视第一次,图片大小倍数为3倍
第二次为4倍
第三次为5倍 
组成拆解:仅图片切换
若想要进行复现,头部为随机触发的三角函数,振幅为1,震颤一次后归位。
我们视角的右手可以通过利用三角函数改变xscale来实现。

组成拆解:身体(body)+ 左翅膀(leftwing)+ 右翅膀(rightwing)+ 尾巴上的圆圈天线(antenna)
以防你不知道,antenna的意思就是天线,这是一个常用单词。
公式整理:
老滑头的计时器比较特殊,这里特地说明:
这是一个套了三角函数的特殊计时器
siner = 2 * cos(time / 24)
还有套了三角函数计时器的三角函数计时器
g = sin(siner / 6)
gg = sin(siner / 12)
---------------------------
身体:
x:不变
y:ys + 8 * sin(siner / 12)
右翅膀:
x:x + 46
y:y + 106 + 2 * g
xscale:2
yscale:1.7 - g * 0.3
左翅膀:
x:x + 82
y:y + 174
xscale:1.95 - g * 0.05
yscale:1.8 - g * 0.2
天线:
x:x + 54 + gg * 2
y:y + 4
角度:20 - gg * 20 
组成拆解:由于我这个版本没有愤怒喵喵,率先推测是单个图片构成的

组成拆解:单个图片
公式整理:
y:ys + 9 * sin(timer / 30) 
组成拆解:身体外壳(body)+ 鸭子(duck)+ 头部(head)+ 脸(face)+ 尾巴摇杆(tail)+ 水(water)+ 玻璃罩(hanger)
公式整理:
玻璃罩:保持不动
鸭子:
x:x + 46
y:每15帧切换上下状态,每次鸭子都会以每帧1个像素做上下移动
水1:
x:x + 40 - 4 * sin(time / 10)
水2:
x:x + 40 + 4 * sin(time / 10)
两个水的y相同
摇杆(发条):
x:x + 88
y:y + 50
头部和脸部很难直接写出公式,这里简单介绍一下
脸和头的运动方向相反,而且脸的速度是头的二倍 如果我要写的话,那就太逆天了:



组成拆解:果冻花(up)+ 底层(stem)
公式整理:
底座:
x:x + 8 * sin(timer / 10)或x + 8 * cos(timer / 10)
果冻花:
x:x + 4 * cos(timer / 10) 
有人曾说,提米的身体和脸是两个生物。现在,让我们从代码层面看看这两个生物的底层逻辑是什么
组成拆解:身体(body)+ 脸部(face)
公式整理:
身体:
x:xs + random(r)
y:ys + random(r)
脸依旧是这个公式,加上random(r)
但是有一点,当时间大于10秒,而且脸小于身体x+100的位置
脸会以每帧0.01像素的速度向右偏移
如果你要看到脸跑到最大位置,这个时间大概是:
100 / 0.01 = 10000帧 = 10000/30 = 333.33秒
你需要挂机五分半才能看到脸停下了
当然,你不能忽略了身体也在乱动的事实
关于r:
当r小于2的时候,r会每帧增加0.01
如果攻击,r清零重新计时 好吧,看来古老的传说是真的呢~

组成拆解:头部(head)+ 上半躯干(torso)+ 下半躯干(legs)+ 底座(base)
公式整理:
一样的,在此之前,我们先看看大表哥的计时器
rot = 30 * sin(time / 6)
-----------------------
头部:
x:x + 5
y:y + 75
角度:-rot
上半躯干:
x:x
y:y + 35 + rot / 4
角度:rot / 2
下半躯干:
x:x + 5
y:x + 65
角度:rot / 3
底座:
x:x - rot / 3
y:y + rot / 3
角度:rot 大表哥越晃越快的操作也很简单,只需要增加rot的值即可,比如rot *= 3

组成拆解:底座(agent)+ 头部(hide/swim/sing)
公式整理:
底座保持不动
头部:
y:sy + 4 * sin(time / 30) 
组成拆解:火山外壳(body)+ 脸部(face)+ 溢出的岩浆(lava)+ 前脚(feetfront)+ 后脚(feetback)
公式整理:
后脚:
x:x + 2 + 2 * cos(time / 6)
y:y + 102 + 2 * sin(time / 6)
前脚:
x:x + 2 + 2 * sin(time / 6)
y:y + 102 + 2 * cos(time / 6)
脸部:
x:x + 26 + 18 + 7 * sin(time / 12)
y:y + 50
xscale:2 - 0.2 * abs(sin(time / 12))
岩浆:
x:x + 42
y:y + 10
身体:
x:x
y:y 
组成拆解:飞机本体(plane)+ 荷叶边洋帽(hat,疑似ZUN帽)+ “尾气”(cloud)+ 腮红(blush)
关于疑似ZUN帽:

我怎么看怎么像大小姐戴的那种,先不管了。
公式整理:
身体:
x:x + 94
y:y + 76 + 3 * sin(time / 7)
帽子:
x:x + 154
y:y + 90 + 3 * sin(time / 7)
腮红:
x:x + 94 + 2 * sin(time / 7)
y:y + 76 + 4 * sin(time / 7)
透明度:0.7 + 0.1 * sin(time / 4)
尾气:
角度时刻+1
x持续加sin(time / 2)
y持续加cos(time / 2)
大小倍率持续加0.05 
组成拆解:左脚(footl)+ 右脚(footr)+ 冒火的头(head/head2)+ 六个单层身体(body)
特地说明:六个单层身体的本质是toby把六个不同长度的环叠到了同一个贴图组,所以实际制作的过程中也确实是六个不同的图片,而不是单个图片完成放缩操作,如果想要复现,只需要修改xscale即可。
公式整理:
首先还是喜闻乐见的特殊计时器声明时间:
nowy = y + 100 - 20 * abs(-sin(time / 14))
----------------------------
左脚:
x:x + 38
y:nowy + 20
角度:40 * abs(-sin(time / 14))
右脚:
x:x + 60
y:nowy + 20
角度:-40 * abs(-sin(time / 14))
身体层:
假设身体从上到下六层的编号(i)依次为0到5。
从最下方开始绘制:
x:x
y:nowy - i * (12 + 6 * abs(sin(time - i / 14)))
头部(正常):
x:x + 50
y:nowy - 30
角度:4 * sin(time / 6) + 12 * sin(hurttime)
以防你不知道,火岩绳被打了之后会换一套动画
如果攻击了火岩绳,那么hurttime开始+1,time定格为28
头部(受伤):
x:x + 50
y:nowy - 30
角度:12 * sin(hurttime) 
(光看这个动画就知道三角函数量多大了。)
组成拆解:中间两条胳膊(midarm1/midarm2)+ 手里拿的茶壶(teapot)+ 下面摆动的手(lowarm)+ 胸前的蝴蝶结(shirt)+ 两条腿(legs)+ 蝴蝶结下面部分的裤子(pants)+ 两条比较高的胳膊(upperarm)+ 肩膀(shoulder,这部分指的是比较高的胳膊的大臂到袖口)+ 晃动的短双马尾(hair)+ 头部(head,这部分不包括眼睛)+ 中间的眼睛(eyecen)+ 两侧的大眼睛(eyebig)+ 两边中间的小眼睛(eyemed)
(作者写完这一长串已经吓哭了)
公式整理:
首先一如既往的计时器:
heady = y + 4 * sin(time / 5)
-----------------------
比较高的胳膊:
x:x + 14
y:y + 86 + 26 + cos(time / 5)
角度:-6 * sin(time / 5)
x:x + 78
y:y + 86 + 26 + cos(time / 5)
角度:6 * sin(time / 5)
肩膀和大臂:
x:x + 42
y:y + 86 + cos(time / 5)
x:x + 50
y:y + 86 + cos(time / 5)
短双马尾:
x:x + 80
y:heady * 1.02 + 18
角度:25 * sin(time / 5)
x:x + 12
y:heady * 1.02 + 18
角度:-25 * sin(time / 5)
头部:
x:x
y:heady
腿部:
x:x + 30
y:y + 162
最低的胳膊:
如果sin(time / 5) < 0,那么切换另一张图片
否则继续切换回来
x:x + 26
y:y + 130 + sin(time / 5)
角度:8 * sin(time / 5) - 8
x:x + 64
y:y + 130 + sin(time / 5)
角度:-8 * sin(time / 5) + 8
拿茶壶的胳膊(大臂):
x:x + 12
y:y + 116 + 2 * cos(time / 5)
x:x + 80
y:y + 116 + 2 * cos(time / 5)
拿茶壶的胳膊(小臂):
x:x + 12
y:y + 130 + 2 * cos(time / 5)
角度:3 * sin(time / 5)
x:x + 80
y:y + 130 + 2 * cos(time / 5)
角度:3 * sin(time / 5)
蝴蝶结:
x:x + 28
y:y + 92 + 2 * sin(time / 5)
裤子:
x:x + 20
y:y + 114 + sin(time / 5)
五只眼睛:
x:x + 24, 30, 42, 62, 68
y:heady + 26, 32, 42 注:如果你想知道五只眼睛的动画,下面是源代码和解释:
for (i = 0; i < 5; i += 1)
{
if (anim > (0 + i * 5) && anim < (7 + i * 5))
eye[i] += 0.5
if (anim > (12 + i * 5) && anim < (16 + i * 5))
eye[i] -= 1
if (anim > 70 && anim < 77)
eye[i] += 0.5
if (anim > 88 && anim < 95)
eye[i] -= 0.5
} 第一个if提取公因式i * 5,所以每只眼睛享有0-7的区间睁眼。
第二个if提取公因式i * 5,每只眼睛会在12-16帧闭眼。
依次循环到70-77区间,所有眼睛睁眼;然后在88-95所有眼睛闭眼。
下面这个表格很清晰的展示了整个动画的时间线:


组成拆解:头部(head)+ 盔甲(armor)+ 盔甲手臂(baraball)+ 腿部(baralegs)+ 靴子(barashoes)+ 握拳的手(barafist)+ 拿剑的手,也包括剑(falchion)
是的,守卫的胳膊是通过两个球进行连接的,所以一共四个球都有自己的公式。
公式整理:
胳膊:
x:x + 110/120
y:y + 40/60 + 3 * sin(timie / 4)
x:x - 10/20
y:y + 40/60 + 3 * sin(timie / 4)
拿剑的手:
x:x + 140
y:y + 80 + 16 + 4 * cos(time / 4)
角度:-30 + 4 * sin(time / 4)
握拳的手:
x:x - 10
y:y + 80 + 16 + 4 * cos(time / 4)
角度:4 * sin(time / 4)
腿部:
x:x + 64
y:y + 100 + sin(time / 4)
xscale:2 + 0.05 * sin(time / 4)
yscale:2 - 0.05 * sin(time / 4)
靴子:
x:x + 64
y:y + 142
xscale:2 + 0.1 * sin(time / 4)
yscale:2 - 0.05 * sin(time / 4)
盔甲:
x:x
y:y + 10 + 2 * sin(time / 4)
头部:
x:x
y:y - 30 + 4 * sin(time / 4) 小知识:01和02用的是同一套动作。

组成拆解:身体(body,正反)+ 头部(head)+ 脸部(face)+ 手臂(arm)+ 帽子(hat)+ 腿部(leg)+ 尾巴(tail)+ 长耳朵(ear)
公式整理:
尾巴:
x:x + 46 * sin(time / 12)
y:y + 88
xscale:2 * sin(time / 12)
身体:
x:x
y:y + 64 + 2 * sin(time / 6)
腿:
x:x ± (2 + 2 * sin(time / 6))
y:y + 128
yscale:2 + 0.1 * cos(time / 3)
头部:
x:x
y:y + 3 * sin(time / 6)
脸部:
x:x
y:y + 12 + 4 * sin(time / 6)
双耳:
x:x - 32; x + 32
y:y - 22 + sin(time / 6)
帽子(如果戴帽子了):
x:x
y:y - 20 + 5 * sin(time / 6)
胳膊:
由于有不同的状态,这里分开阐述
默认戳手:
x:x - 44 + 2 * sin(time / 3)
y:y + 32
x:x + 46 - 2 * sin(time / 3)
y:y + 32
手中抓画本时,和上述公式完全一致
双手在后面时,和上述公式完全一致
单手背后单手画画时:
x:x - 44 + 2 * sin(time / 3)
y:y + 32
x:x + 52 - 2 * sin(time / 3)
y:y + 28 
组成拆解:头部(head)+ 身体(body)
公式整理:
头部:
x:x + 60 + 10 * sin(time / 6)
y:y + 20 + 4 * sin(time / 3)
角度:-2 * sin(time / 6)
身体:
x:x + 60
y:y + 20 + 90
yscale:2 - 0.15 * sin(time / 3) 
组成拆解:身体(body)+ 头部(head)+ 翅膀(wing)
公式整理:
特殊计时器:goof
goof = sin(time / 5)
---------------------
翅膀:
x:x + 60; x + 14
y:y + 40 - goof * 2
角度:±(15 - 30 * sin(time / 2.5))
身体:
x:x
y:y + 50 + goof * 6
头部:
x:x + 6
y:y + goof * 8 
组成拆解:头部(anim2)+ 手臂(hand)+ 腿部(leg)
公式整理:
由于这个计时器较为复杂
我们先说手臂和腿部的公式:
手臂:
x:x + 8 + 2 * sfactor * sin(time / 6)
y:y + 64 + 2 * sfactor * cos(time / 6)
yscale:1.8 + 0.2 * sfactor * cos(time / 6)
x:x + 92 - 2 * sfactor * sin(time / 6)
y:y + 64 - 2 * sfactor * cos(time / 6)
yscale:1.9 + 0.2 * sfactor * cos(time / 6)
腿部:
x:x + 30 + 2 * sfactor * sin(time / 6)
y:y + 84 + 2 * sfactor * sin(time / 6)
yscale:1.8 + 0.2 * sfactor * cos(time / 6)
x:x + 70 - 2 * sfactor * sin(time / 6)
y:y + 84 - 2 * sfactor * sin(time / 6)
yscale:1.8 - 0.2 * sfactor * cos(time / 6) 接下来是神秘计时器部分,先上源码,再做解释:
anim += 1
if (anim > 250)
{
if (sfactor < 1)
sfactor += 0.05
siner += 1
}
if (anim > 140 && anim < 180)
{
anim2 += 1
if (anim2 > 18)
anim = 200
}
if (anim > 220 && anim < 280)
{
anim2 += 1
if (anim2 >= 33)
anim = 300
}
if (anim > 480)
{
if (sfactor > 0)
sfactor -= 0.07
anim2 -= 1
if (anim2 <= 0)
{
sfactor = 0
anim2 = 0
anim = 0
}
} 首先,sfactor这个变量,被规定在了0~1中间,这个数控制着散光眼的手臂和腿部晃动幅度,比如散光眼会在动画播放完之后,才开始晃动,本质上改变的就是sfactor的大小。
然后是anim和anim2变量,anim负责不断增加,anim2负责不断减少。执行anim > 480的时候,散光眼张嘴,同时anim2开始减小,经过33 + 250帧之后,重新执行行走动画,具体表格看这里:


组成拆解:靴子(boot)+ 裙子(dress)+ 翻领(lapel)+ 头部(head)+ 巫师帽(hat)+ 宝珠、法球(orb)+ 法球放出来的十字架(orbsprinkle)
公式整理:
计时器一览:
_sin = sin(time / 5)
_cos = cos(time / 5)
_dsin = sin(time / 2.5)
-----------------------
靴子:
x:x + 70 - 23 - 5 * _sin
y:y + 80 - 6 * _sin
角度:-40 - 10 * _sin
x:x + 70 + 19 + 5 * _sin
y:y + 80 - 6 * _sin
角度:40 + 10 * _sin
翻领:
x:x + 70 - 2
y:y + 52 + _sin
角度:-10 * _sin
裙子:
x:x + 70 - 34
y:y + 52 + _sin
yscale:1.8 - 0.2 * _sin
头部:
x:x + 70 - 6 + _sin
y:y + 2 + 8 * _sin
帽子:
x:x + 70 + 2 + _cos
y:y + 4 + 10 * _sin
法球:
x:x + 70 - 44
y:y + 16 + 2 * _dsin
x:x + 70 + 70
y:y + 6 + 5 * _dsin
十字架(每5帧创建一个):
x:x + 70 + 46 + random(18)
y:y - 10 + 4 * _dsin - random(6)
x:x + 70 - 66 + random(18)
y:y + 4 * _dsin - random(6) 
组成拆解:拿矛的手(leftarm)+ 最顶上的牛角头盔(helmet)+ 龙一般的嘴(dragonmouth)+ 会动的胡须(dragonfur)+ 一整个外壳(body)+ 眼睛(dragoneyes)
公式整理:
眼睛:
x:x - 60 + 94 + 6 * sin(time / 10)
y:y + 70
有人说你这只整理了一个啊?
热知识:眼睛的三种形态用的全是这个公式
身体:
x:x - 60
y:y
头盔:
x:x - 60 + 70
y:y + 2
嘴:
x:x - 60 + 98
y:y + 84
胡须:
x:x - 60 + 64
y:y + 96
手臂:
x:x - 60 + animl3
y:y + animl2 toby的定时动画一如既往的稳定发挥。
晚安骑士的招牌动作就是手臂缓慢升起,然后捶地,捶地之后还有一段晃动期,这里仍旧给出表格:




一般人不要碰这个代码,听我的。甚至我看了这些代码都不想讲。

组成拆解:单个图片切换 + 扫描线(scanline)。
扫描线主要用于构建一种画面挤压、扭曲的感觉,其本质其实是贴图的重新绘制:
b = argument0
c = argument1
d = argument2
a += 1
for (i = 0; i < sprite_height; i += 1)
{
a += 1
draw_sprite_part_ext(sprite_index, image_index, 0, i, sprite_width, (1 * sin(a) * d), (x + (sin(a / b)) * c), (y + i * 2), 2, 2, c_white, image_alpha)
} 
组成拆解:下半部分身体(body)+ 头部(head)
公式整理:
没摸的情况下,shaker为1
摸了一次的情况下,shaker为2
摸了三次的情况下,shaker为4
--------------------------
x, y = random(shaker) - random(shaker)
头部也是这样的 
组成拆解:头部(head+bite)+ 身体(body)
还记得福音蟑螂的随机动手吗?死神鸟这里则是切换之后进行嘴巴咬合。
公式整理:
身体:
x:x + 14 + 5 * sin(time / 10)
y:y + 90 + 6 * abs(cos(time / 8)) - 20 * mstretch
yscale:ystretch 请输入文本(这是头部动画):

这里是身体拉伸的动画:


组成拆解:头部(head)+ 身体(body)
公式整理:无言了全是图片切换,不管,哼


组成拆解:单个图片。
雪铁龙母亲的图片并不是按顺序睁眼闭眼的,而是随机在四张图片里面选择一个表情,所以会有现在看到的抽动的效果。
不得不说,这种做法确实天才。
好吧,喜闻乐见的环节又来了,我知道你来这里是想看什么,对吧?
不要着急,我们会从羊妈Toriel开始慢慢往后延续

组成拆解:仅单个图片。

组成拆解:头部(head)+ 身体(body)。
无公式整理,因为Papyrus在战斗中不会有任何晃动。

组成拆解:头发(hair)+ 脸(face)+ 盔甲(armor)+ 腿部(legs)+ 裤子(pants)+ 拿矛的手(leftarm)+ 握拳的右手(rightarm)
公式整理:
头发y:ys + 4 * sin(time / 6)
头部y:ys + 2 * sin(time / 6)
盔甲y:ys + 4 * sin(time / 6)
裤子y:ys + 2 * sin(time / 6)
左臂:
x:xs + 5 * sin(time / 6)
y:ys + 5 * sin(time / 6)
右臂:
x:xs - 2 * sin(time / 3)
y:ys + 6 * sin(time / 6) + 2 * sin(time / 3) 
组成拆解:和中立路线一样,唯独多了一个眼睛放光(eyebeam)。
公式整理:
头发:
x:x + 85
y:y + 3 * sin(time / 6) + heady + 4
角度:70 - 15 * sin(time / 6)
腿部:
x:x + 100
y:y + 164
左臂:
x:x + 64 + 5 * sin(time / 6)
y:y + 78 + 5 * sin(time / 6)
右臂:
x:x + 136 + 3 * sin(time / 3)
y:y + 78 + 6 * sin(time / 6) + 2 * sin(time / 3)
盔甲:
x:x + 100
y:x + 78 + 4 * sin(time / 6)
角度:-4 * sin(time / 6)
裤子:
x:x + 100
y:y + 122 + 2 * sin(time / 6)
角度:2 * sin(time / 6)
头部:
x:x + 100
y:y + 28 + 2 * sin(time / 6) + heady
激光:
x:x + 110
y:y + 24 + 2 * sin(time / 6)
xscale:(etime - 10) / 4
yscale:2.5 - (etime - 10) / 20
角度:-32 * sin(time / 14)
透明度:1.5 - (etime - 10) / 20 etime是一个持续增加的变量,每帧加1,如果大于等于40那么归零。

组成拆解:双臂(arms)+ 铁盒子外壳(nuts)
图片里所示的双臂是arms2,一共有五个不同的双臂图片。
公式整理:
特殊计时器:
sinvalue = 2 * sin(time / 3)
----------------------
外壳:
x:x + sinvalue
y:y
角度:sinvalue
双臂:
x:x + sinvalue
y:y - 5 * sin(time / 4)
角度:sinvalue

组成拆解:腿部(leg)+ 身体(body)+ 肚子上的心(heart)+ 手臂(arm)+ 脸部(mettface)
公式整理:
特殊计时器:
ds1, ds2 = random(dsf * 2) - dsf
腿部:
x:x + 90 + xoffr
y:y + 120 + yoffr - legh - 0.05 * sin(time / 2)
yscale:2 - 0.05 * sin(time / 3.5)
角度:10 * sin(time / 7) - offangle
x:x + 90 - xoffl - 32
y:y + 120 + yoffl - legh - 0.05 * sin(time / 2)
yscale:2 - 0.05 * sin(time / 3.5)
角度:10 * sin(time / 7)
手臂:
x:x + 36 + sin(time / 3.5)
y:y - legh + 80 + 2 * cos(time / 3.5)
x:x + 110 + sin(time / 3.5)
y:y - legh + 80 + 2 * cos(time / 3.5)
身体:
x:x + 72 + sin(time / 3.5) + ds1
y:y + legh + 134 + 2 * cos(time / 3.5) + ds2
心:
x:x + 72 + sin(time / 3.5) + ds1 + 66
y:y + legh + 134 + 2 * cos(time / 3.5) + 108 + ds2
脸部:
x:x + 68
y:y + 40 - legh + 2 * cos(time / 3.5) 由于mtt ex腿部和手部动作十分丰富,这里进行阐述:
if (noleg == 0)
{
if (legr == 0)
{
legrsprite = spr_mettleg1
xoffr = -14
yoffr = 10
legrh = 36
}
if (legr == 1)
{
legrsprite = spr_mettleg2
xoffr = -16
yoffr = 6
legrh = 8
}
if (legr == 2)
{
legrsprite = spr_mettleg3
xoffr = -10
yoffr = 14
legrh = 60
}
if (legr == 3)
{
legrsprite = spr_mettleg4
xoffr = -10
yoffr = 14
legrh = 30
}
if (legr == 4)
{
legrsprite = spr_mettleg5
xoffr = -18
yoffr = 2
legrh = 42
}
if (legr == 9)
{
legrsprite = spr_mettleg2_flip
xoffr = 20
yoffr = 6
legrh = 8
}
if (legr == 10)
{
legrsprite = spr_mettleg2_flip_v
xoffr = 15
yoffr = 2
legrh = 0
}
if (legl == 0)
{
leglsprite = spr_mettleg1
xoffl = -14
yoffl = 10
leglh = 36
}
if (legl == 1)
{
leglsprite = spr_mettleg2
xoffl = -16
yoffl = 6
leglh = 8
}
if (legl == 2)
{
leglsprite = spr_mettleg3
xoffl = -10
yoffl = 14
leglh = 60
}
if (legl == 3)
{
leglsprite = spr_mettleg4
xoffl = -10
yoffl = 14
leglh = 30
}
if (legl == 4)
{
leglsprite = spr_mettleg5
xoffl = -18
yoffl = 2
leglh = 42
}
if (legl == 9)
{
leglsprite = spr_mettleg2_unflip
xoffl = -5
yoffl = 2
leglh = 6
}
if (legl == 10)
{
leglsprite = spr_mettleg2_unflip_v
xoffl = 0
yoffl = 5
leglh = 2
}
} 大概意思就是:每个不同贴图的腿,它的x, y都是不一样的。
然后接下来是胳膊:
if (arml == 0)
armlsprite = spr_mettarm1
if (arml == 1)
armlsprite = spr_mettarm2
if (arml == 2)
armlsprite = spr_mettarm3
if (arml == 3)
armlsprite = spr_mettarm4
if (arml == 4)
armlsprite = spr_mettarm5
if (arml == 5)
armlsprite = spr_mettarm6
if (arml == 6)
armlsprite = spr_mettarm7
if (arml == 7)
armlsprite = spr_mettarm8
if (armr == 0)
armrsprite = spr_mettarm1
if (armr == 1)
armrsprite = spr_mettarm2
if (armr == 2)
armrsprite = spr_mettarm3
if (armr == 3)
armrsprite = spr_mettarm4
if (armr == 4)
armrsprite = spr_mettarm5
if (armr == 5)
armrsprite = spr_mettarm6
if (armr == 6)
armrsprite = spr_mettarm7
if (armr == 7)
armrsprite = spr_mettarm8 虽然我不知道toby这么列举下来是何意味,但是很容易看懂就是了。

组成拆解:头部(face)+ 身体(body)+ 炮筒手臂(arml)+ 另一个手(armr)+ 背后的翅膀(burst,也许也是灯光)+ 腿部(legs)
公式整理:
翅膀:
x:x - 24或x + 28
y:y + 18 + sin(time / 3)
角度:±2 * sin(time / 6)
透明度:0.4 + 0.5 * abs(sin(time * 0.3))
腿部:
x:x
y:x + 84 + 112
yscale:2 - 0.05 * sin(time / 3)
炮筒手(左边的手):
x:x - 26 - 2 * sin(time / 3)
y:y + 40
角度:-2 * sin(time / 6)
右边的手:
x:x + 40 + 2 * sin(time / 3)
y:y + 40
角度:2 * sin(time / 6)
身体:
x:x + 4
y:y + 36 + 2 * sin(time / 3)
头部:
x:x
y:y + 3 * sin(time / 3) 
组成拆解:头部(head)+ 身子(body)+ 腿部(legs)
我说圈内制作者做了这么多年sans早知道sans的组成拆解了。
公式整理:
核心摆动控制
// 摆动控制变量
y = global.idealborder[2] - 130 // 基础Y坐标
xoff = 0 // X轴摆动偏移
yoff = 0 // Y轴摆动偏移
siner = 0 // 正弦计时器
headx = 0 // 头部X偏移(用于攻击动画)
heady = 0 // 头部Y偏移(用于攻击动画)
// 摆动模式选择(bounce = 0-3)
if (bounce == 3) {
// 模式3:大幅垂直摆动(疲惫状态)
siner += 1
yoff = (sin(siner / 18)) * 2
xoff = 0
}
if (bounce == 2) {
// 模式2:中等幅度垂直摆动
siner += 1
yoff = (sin(siner / 15)) * 4
xoff = 0
}
if (bounce == 1) {
// 模式1:轻微8字形摆动(基础闲晃)
siner += 1
yoff = sin(siner / 3) // 快速垂直摆动
xoff = cos(siner / 6) // 慢速水平摆动,形成8字形轨迹
}
if (bounce == 0) {
// 模式0:静止
siner = 0
yoff = 0
xoff = 0
}
身体绘制
// 绘制躯干(摆动影响)
if (movearm == 0) // 仅在非手臂动画时
draw_sprite_ext(spr_sansb_torso, global.flag[20],
(x + xoff), // X坐标:基础X + 摆动偏移
(y + 42 + yoff / 1.5), // Y坐标:基础Y + 42 + 1/1.5的摆动偏移
2, 2, 0, c_white, 1)
脸
// 绘制面部(有多种状态)
if (facetype == 0) // 正常面部
draw_sprite_ext(spr_sansb_face, global.faceemotion,
(x + xoff + headx), // X坐标:基础X + 摆动偏移 + 头部攻击偏移
(y + yoff + heady), // Y坐标:基础Y + 摆动偏移 + 头部攻击偏移
2, 2, 0, c_white, 1)
// 流汗动画(3种等级)
if (sweat == 1)
draw_sprite_ext(spr_sansb_face_sweat, 0, (x + xoff + headx), (y + yoff + heady), 2, 2, 0, c_white, 1)
if (sweat == 2)
draw_sprite_ext(spr_sansb_face_sweat, 1, (x + xoff + headx), (y + yoff + heady), 2, 2, 0, c_white, 1)
if (sweat == 3)
draw_sprite_ext(spr_sansb_face_sweat, 2, (x + xoff + headx), (y + yoff + heady), 2, 2, 0, c_white, 1)
// 蓝眼特效(特殊攻击时)
if (facetype == 1) {
f_i += 1
draw_sprite_ext(spr_sansb_blueeye, floor(f_i / 2),
(x + xoff + headx), (y + yoff + heady), 2, 2, 0, c_white, 1)
} 
组成拆解:披风/斗篷(cape)+ 胳膊(ballarm)+ 左边的胳膊(arml)+ 右边的胳膊(armr)+ 头部(head1)+ 带有符文标志的盔甲(armor)+ 下面三个角的裙子(dress)+ 腿部(legs)+ 脚(feet)
我们先来看点位图解(这个是我用Python模拟的):

Asgore的绘制分为两部分,一部分绘制头、身子、腿、脚;另一部分由三叉戟控制,绘制手臂、手、三叉戟
公式整理(三叉戟):
三叉戟:
x:x
y:y + 0.3 * sin(time / 15)
角度:0.02 * sin(time / 15)
手臂:
x:part[5].x + 14 + body.x
y:part[5].y + 64 + body.y
角度:由自身(x, y)指向(x - 55 * 0.02 * cos(time / 15), y - 55 * 0.02 * sin(time / 15))
xscale:和角度目标点进行勾股定理距离计算后除以40
x:part[4].x + 34 + body.x
y:part[4].y + 64 + body.y
设rdistx, rdisty, armlen
令rdistx = x + 110 * cos(0.02 * sin(time / 15))
rdisty = y + 110 * sin(0.02 * sin(time / 15))
armlen = (x, y)和(rdistx, rdisty)的距离
若armlen > 100
那么令armoff = (armlen - 100) / 2
rdistx = x + (110 - 2 * armoff) * cos(0.02 * sin(time / 15))
rdisty = y + (110 - 2 * armoff) * sin(0.02 * sin(time / 15))
随后角度和长度计算使用(x, y)和(rdistx, rdisty)
右手:
x:rdistx
y:rdisty
角度:sin(time / 15)
左手:
x:x - 55 * 0.02 * cos(time / 15)
y:y - 55 * 0.02 * sin(time / 15)
角度:sin(time / 15) 剩下的部分:
脚:依旧静止不动
腿:仍然静止不动
下面为了方便就不写xy了,xy在上面的python图里,直接看幅度:
头部:0.3
身体:0.2
裙子:0.1
双手:再加0.1
披风:0.05 敌人的砍击震动也是有公式的:
首先敌人有一个特殊变量shudder,这个变量就是总共的幅度,会被固定设置为16(前提是小怪:sans直接跑、羊妈剧烈震动,完全不符合这个)。
然后每次震颤会固定减小2,然后左右对换:
x += shudder
if (shudder < 0)
shudder = (-((shudder + 2)))
else
shudder = (-shudder)
if (shudder == 0)
{
global.hurtanim[myself] = 2
exit
}
alarm = 2 比如一开始是16,先往右移16
随后因为shudder > 0,取负数-16
然后由于shudder < 0,取负数并且减小2,变成14
再取负数-14……
直到0,动画结束。