從零開始的MC特效
本教程轉(zhuǎn)自 [Tutorial][G-Second][應該全核心]從零開始的MC特效
原作者是我 (小聲
目錄:
- 導讀
- 數(shù)學與Minecraft的融合
- 利用數(shù)學在Minecraft中畫一個圓
- 利用數(shù)學在Minecraft中畫一個球
導讀
本教程需要讀者有一定的空間想象能力(因為我也懶得畫圖了233)
本教程使用的 Spigot1.10.2-R0.1-SNAPSHOT 核心
在閱讀之前請確保你具有高中數(shù)學必修4和Java基礎(chǔ)的知識
(沒有我也會適當?shù)慕忉尩?
<初中生>: 如果你是初中的話匠楚,別慌峻厚,你有函數(shù)的概念就可以讀懂本教程(應該吧...)
<高中生>: 如果你還未學到關(guān)于上面的兩本書,別慌學到了再來看也行233 (霧
數(shù)學與Minecraft的融合
首先我們都知道Minecraft是一個3D游戲韭寸,所以它就有了XYZ這三個軸,那么我們可以看如下的一張圖來了解一下
本教程暫不涉及關(guān)于Y軸的相關(guān)內(nèi)容潦匈,所以我們可以先從平面直角坐標系來分析
利用數(shù)學在Minecraft中畫一個圓
以下內(nèi)容需要sin函數(shù)與cos函數(shù)的相關(guān)知識烘嘱!
首先呢我們先來看一張圖(自己用word畫的2333)
分析:
首先這個坐標系有一個單位圓(半徑為1的圓),然后我們看到角α為30°尚粘,之后點P的橫坐標為 √3/2 縱坐標為 1/2
然后我們再看下圖
那么我們是否可以這么認為择卦,點P的橫坐標其實是 cos(30°) 而縱坐標就是 sin(30°)呢?
補充: 如果你已經(jīng)有了參數(shù)方程的概念那么郎嫁,這里你可以忽略了 —— 2019/1/12
PI 代表圓周率π, 之后π = 180° (別問我為什么秉继,課堂上自己學去233)*
P(cos(30°), sin(30°)), 弧度制: P(cos(PI/6), sin(PI/6))
那么P的橫坐標和縱坐標都是可以利用函數(shù) cos和sin 求出泽铛,那么我們?yōu)槭裁床豢梢?strong>遍歷一下把360°全部都給算出呢尚辑?所以我看寫出下方的代碼這樣我們就可以把一周角里所有的角度都給遍歷了一便,并且我們都算出了每個角度所對應的cos值和sin值吧盔腔,然后我們需要把他們作用到Minecraft當中
// 我們把玩家腳下的location作為是原點O
Location location = player.getLocation();
for (int degree = 0; degree < 360; degree++) {
double radians = Math.toRadians(degree);
double x = Math.cos(radians);
double y = Math.sin(radians);
}
那么在上圖的for循環(huán)語句塊中我們有兩個變量 x y杠茬,也就是 P(x, y) 吧,之后我們回頭看一下for循環(huán)語句塊外的那個變量location弛随,那個我們可以理解成是在上圖中的原點O瓢喉,那么我們做個假設(shè),我們需要把點P轉(zhuǎn)換成MC中的Location要怎么做撵幽?灯荧,其實很簡單
location.add(x, 0, y);
我們把location的X軸假想為0, Z軸假想為0(這里的X軸和Z軸指的是Minecraft中的那兩個軸)即圖中原點O為(0, 0)礁击,那么在Minecraft中不可能任何時候原點的X Z軸都是0盐杂,所以我們需要做相加的操作
(上面可能會聽得一頭霧水,簡單來說當原點O不為(0, 0)時哆窿,假設(shè)為(2, 2)链烈,那么我們要做的是給玩家的周圍建立圓吧,那么這時候點P的坐標應該為 P(2 + x, 2 + y))
要是還聽不懂的話那就去喝杯茶挚躯,洗個澡吧2333
那么我們可以做以下的操作了
location.add(x, 0D, y);
// 播放粒子
location.getWorld.playEffect(location, Effect.HAPPY_VILLAGER, 1);
// 為什么要減强衡?因為我們要確保原點是不變的狀態(tài)才可以哦~
location.subtract(x, 0D, y);
游戲內(nèi)效果
完整代碼
// 我們把玩家腳下的location作為是原點O
Location location = player.getLocation();
for (int degree = 0; degree < 360; degree++) {
double radians = Math.toRadians(degree);
double x = Math.cos(radians);
double y = Math.sin(radians);
location.add(x, 0, y);
location.getWorld().playEffect(location, Effect.HAPPY_VILLAGER, 1);
location.subtract(x, 0, y);
}
利用數(shù)學在Minecraft中畫一個球
首先我們來觀察一下sin的函數(shù)圖像,具體如下
從上圖可以看出 sin函數(shù) 始終在 1~-1 之間徘徊码荔,所以我們認為它是有周期性的漩勤,那么這跟球的生成有什么聯(lián)系呢?我們看下圖
首先這是一個球?qū)Π伤踅粒缓竽匚以谇蛏袭嬃藥讉€橫截面(才不是什么旋風)出來越败,那么通過上圖我們是不是可以得出一個結(jié)論,一個球體其實是由無數(shù)個圓構(gòu)成的硼瓣?只是它們的半徑不同對吧究飞。那這跟sin函數(shù)有啥聯(lián)系呢?
首先我們回到sin的函數(shù)圖像,我們看當x在0~π之間時連起來的y軸是不是像一個半圓耙诟怠媒峡?而且它們的半徑(這里的半徑可以理解為sin函數(shù)中的y軸)也是不同的,那么我們是不是可以這么認為葵擎,我們只需要 0 ~ ****π 之間的x值谅阿,然后代入函數(shù)當中就可以求出對應的y軸的值了?
那么 0 ~ π 是什么值呢酬滤?其實在上面的圓中我就講過 π=180°奔穿,所以我們求得其實就是 sin(0 ~ 180°)。
那么有了上面的思路我們可以求出每個圓的半徑對吧敏晤,那么我們寫出下面的代碼
for (double i = 0; i < 180; i++) {
double radians = Math.toRadians(i);
double radius = Math.sin(radians);
}
在上方的代碼當中我們求出了一個球中每個圓的半徑, 但是我們還需要考慮一件事贱田,我們是不是要規(guī)定一下每個圓之間的距離啊嘴脾?
那么我們引入cos的函數(shù)圖像
從上圖可以看出 f(x) = cos(x),
當x=0時, f(x)則為1.
當x=π時男摧,f(x)則為-1.
那么我跟sin的函數(shù)圖像聯(lián)系一下, 在上面的代碼中我們發(fā)現(xiàn),radius的值是從小到大再到小译打,那么我們想一下耗拓,如果半徑是小的那么那個圓是也是小的,而我們要畫的圓是從上往下畫的(觀察圖 2-2)對吧奏司,所以我們是不是要給那個最小的圓的y軸是最高的乔询?(沒看懂?喝杯茶吧)而cos函數(shù)就可以幫我們達到這一點韵洋,所以我們寫出以下的代碼
double y = Math.cos(radians);
那么這樣我們就可以獲得當前for循環(huán)時那個圓的高度了竿刁。
在上面的結(jié)構(gòu)中我們得到了當前圓的半徑和高度,那么我們要怎么通過這兩個東西畫出來呢搪缨?
我們在第三章畫圓時曾經(jīng)做過這么一個操作
for (int degree = 0; degree < 360; degree++) {
double radians = Math.toRadians(degree);
double x = Math.cos(radians);
double y = Math.sin(radians);
}
上方的代碼中我們只能制造出一個半徑為1的圓食拜,那么我們想擴大它的半徑需要怎么做?
我們這里又引入一個函數(shù)y=Asin(ωx + φ) (這里的sin也可以為cos)副编,其實這個函數(shù)跟sin函數(shù)差不多只不過多了幾個變量负甸,那么這里我們只需要考慮A的值,
為什么呢痹届?我們來看一下這個函數(shù)在數(shù)學上的定義:
- φ(初相位):決定波形與X軸位置關(guān)系或橫向移動距離(左加右減)
- ω:決定周期(最小正周期T=2π/|ω|)
- A:決定峰值(即縱向拉伸壓縮的倍數(shù))
由于這里我們只考慮A所以我們可以把上方的函數(shù)簡寫為 y = Asin(x)呻待,假設(shè)我們的A為2,那就是sin(x) * 2了队腐,那么反應在函數(shù)圖像上是這樣的
那么有了上面的概念我們不妨使用 Math.cos(x) * 半徑 來擴大本次循環(huán)時所對應的半徑蚕捉,所以我們寫出以下的代碼
for (double j = 0; j < 360; j ++) {
// 依然需要做角度轉(zhuǎn)弧度的操作
double radiansCircle = Math.*toRadians*(j);
double x = Math.*cos*(radiansCircle) * radius;
double z = Math.*sin*(radiansCircle) * radius;
}
那么這樣就可以控制好本次循環(huán)我們需要這個圓多少半徑了,那么我們寫好之后就可以放在Minecraft中看看效果
完整代碼:
for (double i = 0; i < 180; i++) {
// 依然要做角度與弧度的轉(zhuǎn)換
double radians = Math.toRadians(i);
// 計算出來的半徑
double radius = Math.sin(radians);
double y = Math.cos(radians);
for (double j = 0; j < 360; j++) {
// 依然需要做角度轉(zhuǎn)弧度的操作
double radiansCircle = Math.toRadians(j);
double x = Math.cos(radiansCircle) * radius;
double z = Math.sin(radiansCircle) * radius;
location.add(x, y, z);
location.getWorld().playEffect(location, Effect.HAPPY_VILLAGER, 1);
location.subtract(x, y, z);
}
}
游戲內(nèi)的效果
然后你就會發(fā)現(xiàn)你的游戲卡得一匹2333香到,因為我們是在360°全方位的進行渲染粒子的操作2333鱼冀,但實際業(yè)務(wù)中我們可能并不需要做這種需求报破,那么我們就需要做一個關(guān)于跳躍的操作唄,我們看下面的代碼
for (double i = 0; i < 180; i += 180 / 6) {
// 依然要做角度與弧度的轉(zhuǎn)換
double radians = Math.toRadians(i);
// 計算出來的半徑
double radius = Math.sin(radians);
double y = Math.cos(radians);
for (double j = 0; j < 360; j += 180 / 6) {
// 依然需要做角度轉(zhuǎn)弧度的操作
double radiansCircle = Math.toRadians(j);
double x = Math.cos(radiansCircle) * radius;
double z = Math.sin(radiansCircle) * radius;
location.add(x, y, z);
location.getWorld().playEffect(location, Effect.HAPPY_VILLAGER, 1);
location.subtract(x, y, z);
}
}
跟上面的代碼不同的是我在遍歷的時候修改了步長(step)千绪,那這一個有什么講究呢充易?我們在每一次循環(huán)給 i 和 j就加的是30了對吧,而不是自加1荸型,
那么我們看第一層循環(huán)盹靴,這一層循環(huán)控制的步長其實是我們其實需要多少圈,為什么呢瑞妇?我們看下面的圖來理解一下
這里我為了方便讀者理解我把圖 2-1 旋轉(zhuǎn)了一下稿静,上圖我們假想黑點是玩家的location,那么那幾個紅點就是我們把步長修改后所得到的產(chǎn)物
那么第二層循環(huán)我修改的步長又是什么意思呢辕狰?我們也拿張圖來理解一下上圖中每個角的度數(shù)都是30°改备,那么我修改了步長之后是不是我只會在這幾個黑點上面做playEffect()的操作了?(看不懂的話喝口水再來看233)
修改了步長后游戲內(nèi)的效果:
結(jié)語
內(nèi)容依然是挺少的蔓倍。悬钳。希望能教給讀者一些東西吧233