[Tutorial][数学向]从零开始的MC特效(三 | 向量的旋转)

目录:

  • 导读
  • Yaw,Pitch,Roll与向量的旋转
  • 利用向量旋转一个圆

导读

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

高三时间有限,我就不解释了,请读者见谅! —— 作者 2019/1/12

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


Yaw,Pitch,Roll与向量的旋转

这次我们来讲讲一些概念上的东西,在Minecraft中除了X, Y, Z之外Location还有两个量一个是Yaw一个是Pitch,这两个东西在学术上被称为欧拉角(飞机姿态角)
怎么理解这三个内容呢?

Yaw: 我们 水平旋转 我们的头,也就是左右转头,这就是一次Yaw转动

Pitch: 我们 上下旋转 我们的头,也就是上下点头,这就是一次Pitch转动

Roll: 这个东西在Minecraft里面没有,但是我也讲一下,大家都玩过绝地求生吧,里面的人物QE摇头时就是一次Roll转动

看不懂我说的可以看别的有图的

那么它们跟我们的向量旋转有什么关系呢?其实在Minecraft中,我们利用Yaw和Pitch就可以描述出一个Vector向量,如我们看如下的图


Minecraft Yaw 图.png

在Minecraft当中,Yaw为0时,它表示的就是Z轴正半轴的方向,为-90°时则表示X轴正半轴的方向

Pitch就是你的Steve上下看时的方向,看向天空时最大值是-90,看向地板时最大值是90,正视时就是0

概念的东西都讲完了,我们来谈谈向量的旋转,那么在数学上的话,平面向量的旋转是有直接的公式套用,或者利用矩阵也可以达到目的,点我看相关资料

那么通过上面的资料,我们就可以得到三个方法

    /**
     * 将一个向量围绕X轴旋转angle个角度
     *
     * @param v     向量
     * @param angle 角度
     * @return {@link Vector}
     */
    public static Vector rotateAroundAxisX(Vector v, double angle) {
        angle = Math.toRadians(angle);
        double y, z, cos, sin;
        cos = Math.cos(angle);
        sin = Math.sin(angle);
        y = v.getY() * cos - v.getZ() * sin;
        z = v.getY() * sin + v.getZ() * cos;
        return v.setY(y).setZ(z);
    }

    /**
     * 将一个向量围绕Y轴旋转angle个角度
     *
     * @param v     向量
     * @param angle 角度
     * @return {@link Vector}
     */
    public static Vector rotateAroundAxisY(Vector v, double angle) {
        angle = -angle;
        angle = Math.toRadians(angle);
        double x, z, cos, sin;
        cos = Math.cos(angle);
        sin = Math.sin(angle);
        x = v.getX() * cos + v.getZ() * sin;
        z = v.getX() * -sin + v.getZ() * cos;
        return v.setX(x).setZ(z);
    }

    /**
     * 将一个向量围绕Z轴旋转angle个角度
     *
     * @param v     向量
     * @param angle 角度
     * @return {@link Vector}
     */
    public static Vector rotateAroundAxisZ(Vector v, double angle) {
        angle = Math.toRadians(angle);
        double x, y, cos, sin;
        cos = Math.cos(angle);
        sin = Math.sin(angle);
        x = v.getX() * cos - v.getY() * sin;
        y = v.getX() * sin + v.getY() * cos;
        return v.setX(x).setY(y);
    }

那么如果我们想用Yaw和Pitch来旋转向量应该怎么做呢?这里我直接给出方法,来自开源项目EffectLib里的VectorUtils.java

    /**
     * This handles non-unit vectors, with yaw and pitch instead of X,Y,Z angles.
     * <p>
     * Thanks to SexyToad!
     * <p>
     * 将一个非单位向量使用yaw和pitch来代替X, Y, Z的角旋转方式
     *
     * @param v            向量
     * @param yawDegrees   yaw的角度
     * @param pitchDegrees pitch的角度
     * @return
     */
    public static final Vector rotateVector(Vector v, float yawDegrees, float pitchDegrees) {
        double yaw = Math.toRadians(-1 * (yawDegrees + 90));
        double pitch = Math.toRadians(-pitchDegrees);

        double cosYaw = Math.cos(yaw);
        double cosPitch = Math.cos(pitch);
        double sinYaw = Math.sin(yaw);
        double sinPitch = Math.sin(pitch);

        double initialX, initialY, initialZ;
        double x, y, z;

        // Z_Axis rotation (Pitch)
        initialX = v.getX();
        initialY = v.getY();
        x = initialX * cosPitch - initialY * sinPitch;
        y = initialX * sinPitch + initialY * cosPitch;

        // Y_Axis rotation (Yaw)
        initialZ = v.getZ();
        initialX = x;
        z = initialZ * cosYaw - initialX * sinYaw;
        x = initialZ * sinYaw + initialX * cosYaw;

        return new Vector(x, y, z);
    }
具体的证明过程我这里就不阐述了,请读者自行解决吧...

那么有了上面的基础我们就可以来做一个简单向量旋转的特效

利用向量旋转一个圆

首先我们需要做个分析
如果我们要用向量制作一个围绕Y轴的圆可以怎么做呢?
我们看下方的代码

public void createACircleWithVector(Location loc) {
    double radius = 1D;
    // 我们直接在X轴正半轴上加一个单位, 用来制作我们的第一个向量
    Vector originalVector = getVector(loc, loc.clone().add(1, 0, 0));
    originalVector.multiply(radius); // 圆的半径长度
    for (int degree = 0; degree < 360; degree++) {
        // 我们将向量进行旋转
        Vector vector = VectorUtils.rotateAroundAxisY(originalVector, degree);
        loc.add(vector);
        loc.getWorld().spawnParticle(Particle.FLAME, loc, 0);
        loc.subtract(vector);
    }
}

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

首先我们看这行代码
Vector originalVector = getVector(loc, loc.clone().add(1, 0, 0));
这行要怎么理解呢?我们看下方的图来理解一下

向量OA

首先我们在loc的X轴上增加了一个单位也就是图中的A点,那么我们利用终点减起点来得到向量OA(看不懂的去复习MC特效二),那么这个向量OA我们将做为我们的待旋转向量

之后我们进入循环,我们假设degree是2
Vector vector = VectorUtils.rotateAroundAxisY(originalVector, 2);
那么我们调用该方法即可得到绕Y轴逆时针旋转2个角度后的向量

之后就是坐标加向量,绘制粒子,坐标减去向量保持坐标不变等一系列通法
最终的效果就是这样啦

绕Y轴旋转


那么我们如果想制作一个类似物品掉落时,然后物品围绕Y轴旋转的那种效果,我们又要怎么做呢?这里我们就要利用到Yaw和Pitch

首先我们这里新建一个类用于旋转圆特效的实现,并且让它继承BukkitRunnable,当然Runnable也是可以的

public class RotatableCircle extends BukkitRunnable {

    // 原点
    private Location location;
    // X轴上的单位向量
    private Vector originalVector;

    private float yaw = 0;
    private double radius = 1D;

    public RotatableCircle(Location location) {
        this.location = location.clone();
        // getVector() 方法是上面已经发过的一个方法,目的是构造一个向量OA
        originalVector = getVector(location, location.clone().add(1, 0, 0));
    }

    @Override
    public void run() {

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

从上方的代码我们先简单的需要几个基本量,待旋转向量和原点即可,之后yaw和radius分别是yaw和圆的半径

在构造函数里,我们将传入的location参数进行克隆以防原location的变动
之后我们依然做一个向量OA,当作待旋转向量

之后我们进行几何分析,看下图


向量OA

首先这是一个向量OA,如果我们想让它旋转1个yaw单位可以使用以下方法
Vector vector = VectorUtils.rotateVector(originalVector, 1, 0)

旋转θ个yaw角

那么这就是旋转一个θ yaw角,那么pitch角要怎么旋转呢?
旋转θ个pitch角

根据上方的图我们将向量OA旋转θ个角之后得到了向量OB,那么我们就可以这么写我们的代码

// 请注意第三个参数 θ 是角度
Vector vector = VectorUtils.rotateVector(originalVector, 0, θ)

那么有了上面的铺垫我们可以这么写我们的RotatableCircle

public class RotatableCircle extends BukkitRunnable {

    // 原点
    private Location location;
    // X轴上的单位向量
    private Vector originalVector;

    private float yaw = 0;
    private double radius = 1D;

    public RotatableCircle(Location location) {
        this.location = location.clone();
        originalVector = VectorUtils.getVector(location, location.clone().add(1, 0, 0));
    }

    @Override
    public void run() {
        // 我们假设是第一次yaw旋转, 那么是0, 所以我们先将originalVector旋转yaw个单位
        Vector vectorYaw = VectorUtils.rotateVector(originalVector, yaw, 0);
        // 之后我们将pitch进行 90 ~ -90 的一个循环用于将向量进行上下翻转
        for (float pitch = 90; pitch > -90; pitch--) {
            Vector vector = VectorUtils.rotateVector(vectorYaw, 0, pitch);
            // 这样得出来的vector只有一个半圆, 那么另外一个向量我们可以通过得到相反向量来制造出
            Vector reverseVector = vector.clone().multiply(-1);

            // 在正方向上绘制粒子
            location.add(vector);
            location.getWorld().spawnParticle(Particle.FLAME, location, 0);
            location.subtract(vector);

            // 在反方向上绘制粒子
            location.add(reverseVector);
            location.getWorld().spawnParticle(Particle.FLAME, location, 0);
            location.subtract(reverseVector);
        }

        // 将yaw设定在0~360之间进行循环
        if (yaw >= 360) {
            yaw = 0;
        } else {
            yaw++;
        }
    }
}

我们在代码中像下方一样调用

RotatableCircle rotatableCircle = new RotatableCircle(location);
rotatableCircle.runTaskTimer(你插件的主类实例, 0L, 3L);

最终的效果

最终效果

结语

高三不易,望读者谅解! —— 撰写: 一个来自普高文科的学生

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

推荐阅读更多精彩内容