[Tutorial][数学向]从零开始的MC特效(二 | 向量基础)

从零开始的MC特效 (二)

本教程转自 [Tutorial][Bone Studio][应该全核心]从零开始的MC特效 (二)
原作者是我 (小声

目录:

  • 导读
  • 数学上的向量与BukkitAPI中的向量
  • 利用向量进行画线操作
  • 利用画线进行多边形的绘制
  • 一个五角星

导读

本教程需要读者有一定的空间想象能力(因为我也懒得画图了233)
本教程使用的 Spigot1.10.2-R0.1-SNAPSHOT 核心
在阅读之前请确保你具有高中数学必修4和Java基础的知识
(没有我也会适当的解释的)

<To初中生>: 如果你是初中的话,别慌,你有趋向的概念就可以读懂本教程(应该吧...)
<To高中生>: 如果你还未学到关于上面的那本书,别慌学到了再来看也行233 (雾
<To大学生>: 没什么好说的...


1.数学上的向量与BukkitAPI中的向量

此处只讲平面向量

在教程开始之前我们来谈一下数学上对向量的定义
**定义:
**
向量由长度和方向组成,总是用来描述从一个点到另一个的移动。
和我们平常所说的数量不同的是,向量则是有了方向这一概念
那么我们利用图来理解一下向量

图 1-1

在 图 1-1 当中我们定义两个点一个是点A一个是点B,那么我们以A为起点,B为终点作一条有向线段,得到下图

图 1-2

在 图1-2 当中我们作了一条有向线段,而这条有向线段我们称之为向量AB,那么这就是向量的一个基本定义

向量的坐标表示: 假设A = (1, 3), B = (2, 4),则向量AB = (2 - 1, 4 - 3),即终点减起点
(在BukkitAPI中,向量以坐标来表示所以突出这一段)

我们再来谈谈关于向量的一些相关概念
向量的模: 若我们有一个向量AB,那么它的模可以使用 |AB| 来进行表示,表示的则是AB之间的距离,即向量AB的长度
单位向量: 我们把一个模等于1个单位长度的向量叫做单位向量
相反向量:向量A长度相等,方向相反的向量,叫做向量A的相反向量
零向量: 我们把模长等于0的向量叫做零向量


向量的基本运算

向量是有加和乘的,那么我们根据以下的一些图来了解一下它们的基本运算,

image

向量相加应满足平行四边形定则或三角形定则
若向量A = (1, 3), 向量B = (2, 4),则向量A+向量B = (3, 7)

image

在上方我们给一个向量乘以-1,得到了反方向的向量,这个我们称之为相反向量
若向量A = (1, 3),向量A * 2 = (2, 4)
若向量A = (1, 3),向量A * -1 = (-1, -3)


BukkitAPI上的向量 —— Vector

那么BukkitAPI上对向量的定义又是怎么样的呢?首先我们在 org.bukkit.util 包下找到一个叫Vector的类

那么在BukkitAPI上,Vector类用于表示从一个Location到另一个Location的"趋势"。

若我们想获取从LocationA到LocationB的向量,我们可以使用以下的代码,即终点减起点

/**
 * 取第一个坐标到第二个坐标的向量
 *
 * @param firstLocation  坐标1
 * @param secondLocation 坐标2
 * @return {@link Vector}
 */
public static Vector getVector(Location firstLocation, Location secondLocation) {
    return secondLocation.subtract(firstLocation).toVector();
}

请注意!这里的 secondLocation.subtract() 将会改变secondLocation的值

2.利用向量进行画线操作

首先我们来看张图

图 2-1

图 2-1

在上方的图中,我们定义两个点,一个是A一个是B,那么对应到MC中它们就是两个Location,LocationA和LocationB,那么我们要怎么使用向量来给这两个点进行画线的操作呢?

思路:

  • 首先我们要获取一下这两个点的一个向量,我们称之为向量AB
  • 之后我们把向量AB转为单位向量,方向不变
  • 在Vector类里有个叫multiply的函数,这个函数是对Vector进行乘的操作
  • 若向量AB乘以2,那么它的长度就会乘以了2,之后我们再使用locationA的add函数进行增加Vector,这样我们就可以获得在AB上期中的一个点了,然后我们进行遍历的操作以此类推...(看不懂的话喝杯茶...)

我们把上方的思路转为几何来理解一下


image

有了上面的思路,我们就可以打代码了

首先我们要获取它们向量,这里我调用了location的clone函数,因此不会改变locB的值
Vector vectorAB = locB.clone().subtract(locA).toVector();
之后我们先获取一下该向量的长度,等下要用
double vectorLength = vectorAB.length();
然后我们再把该向量转为单位向量
vectorAB.normalize();
然后我们就需要利用for循环来计算出一个每一个需要multiply的值

for (double i = 0; i < vectorLength; i += 0.1) {
     // 这里如果我们直接进行multiply的话则会修改vectorAB的值,所以我们先clone再multiply
     Vector vector = vectorAB.clone().multiply(i);
}

i的类型:

  • 首先我们来解释为什么这里的i的类型为double
  • 因为在坐标运算中,精确度是比较高的,当两个点相近时可能他们的值还没有到1,可能只有0.8左右的长度,所以我们使用double来修饰

结束值: 之后我们来谈谈这里为什么要循环到向量的模长的时候就结束,在上面的思路我们只举了当i == 2时的一个情况,而我们想获得这两个点之间的点,所以我们要填入向量的模长

步长: 这里我们依然修改了循环的步长,那么它的作用是什么?其实就是在设定每一个add出来后的点的间距 (这里我不懂怎么解释,直接画了个图给你们,按照图来理解会很好哦~)

image

那么我就会得到每一次从点A往点B进行逐步偏移的一个效果,然后我们每次给locA,进行add的操作就可以得到当前循环应该得到的Location

locA.add(vector);
locA.getWorld().playEffect(locA, Effect.HAPPY_VILLAGER, 1); // 这里是播放粒子的代码...
locA.subtract(vector);

完整代码:

public static void buildLine(Location locA, Location locB) {
    Vector vectorAB = locB.clone().subtract(locA).toVector();
    double vectorLength = vectorAB.length();
    vectorAB.normalize();
    for (double i = 0; i < vectorLength; i += 0.1) {
        Vector vector = vectorAB.clone().multiply(i);
        locA.add(vector);
        locA.getWorld().playEffect(locA, Effect.HAPPY_VILLAGER, 1);
        locA.subtract(vector);
    }
}

游戏内的效果:


image

注: 以下的内容则是对于画线的一些实践,跟上方的理论知识有部分的联系,所以可看可不看,不属于应掌握内容

3.利用画线进行多边形的绘制

在第一章的时候,我们利用三角函数画出了一个圆,如果我们向画的是一个多边形该怎么做呢?

这里顺便讲个小故事,在很久以前有个人叫阿基米德,他使用了一种叫做割圆法的方式计算出了圆周率,那么这个割圆法的步骤可以看这个视频来了解一下

那么我们提到这个割圆法有什么用呢,首先这个割圆法利用的是内接六边形,再使用勾股定理求出来的,之后再内接八边形,十六边形一直下去...那么我们多边形始终都离不开一个圆,当然了是内接的多边形,而且还是规则的多边形,那么如果我们要画一个五边形或者三角形该怎么画呢?

首先既然是多边形,就会有点,我们找到点之后就成功了一半,我们来看一张图


图 3-1

在上图中我们找到了五个点,这五个点,每两两之间的夹角都为72°,这72°是怎么来的呢?我们利用360° / 5就可以得到72°,那么从360°/5得到的是一个单位圆中平均分成五块,且每块跟X轴正半轴的夹角都为72的倍数,那么这五个点就是这么找出来的

我们把它放入代码中看看要怎么操作
我们依然使用玩家的location作为原点O,之后我们实例化一个List用于保存这五个点

Location playerLocation = player.getLocation();
List<Location> locations = Lists.newArrayList();

因为我们只是需要72的倍数的角度,所以我们把步长修改为 i += 72 (等价于 i = i + 72)

for (int i = 0; i < 360; i += 72) {
    // 转弧度制
    double radians = Math.toRadians(i);
    locations.add(playerLocation.clone().add(3 * 
    Math.cos(radians), 0D, 3 * Math.sin(radians)));
}

为了以便于观看我将每次cos和sin计算的值都乘以3来扩大点到原点的距离
之后我们的locations里就存放有那5个点了
如果我们现在对这五个点播放粒子的话你可以看到以下这样的图


image

那么我们剩下的就是每个点依次顺序进行画线操作,然后你就可以看到以下的效果了


image

4.一个五角星

其实也没什么好说的...看代码吧...

Location playerLocation = player.getLocation();
List<Location> locations = Lists.newArrayList();
for (int i = 0; i < 360; i += 72) {
    // 转弧度制
    double radians = Math.toRadians(i);
    locations.add(playerLocation.clone().add(3 * Math.cos(radians), 0D, 3 * Math.sin(radians)));
}

buildLine(locations.get(0), locations.get(2));
buildLine(locations.get(0), locations.get(3));
buildLine(locations.get(1), locations.get(3));
buildLine(locations.get(1), locations.get(4));
buildLine(locations.get(2), locations.get(4));

备注: 这里的buildLine是上方我所发出来的一个方法哦

上方的locations的画线操作我们可以利用图来理解一下就可以

image

为了方便解释我给每个点都起了名字括号内的是它在locations中的下标

如果我们要画一个五角星,可以这么画
连接AC, AD,即0 -> 2,0 -> 3
连接BD, BE,即1 -> 3,1 -> 4
连接CE,即 2 -> 4
然后你就可以看到这样的五角星了


image

首先我承认这种方法并不是最好的方法,如果你有更好的方法的话不妨在楼下发出来,大家可以一起讨论讨论

结语

内容还是非常的少...然后...没什么好说的 —— 撰写: 一个来自普高文科的学生

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342