隨便寫的做耐久小教程——(5)基于數(shù)學的一些彈幕舉例

本教程是基于umbrella丶2修改果引擎制作的耐久引擎(稍加改動)的教程,該引擎gm版本為Gamemaker8。?
此章提及一些數(shù)學方法在彈幕設計中的應用,有些彈幕事實上使用頻率并不高,編寫此章的原因僅僅是希望能為讀者帶來一些啟發(fā)
一.均分直線與多邊形
在第(3)章中,我們介紹了以極坐標的方式生成正方形的方法,效果如下

同樣,用極坐標的方式,可以通過如下函數(shù)創(chuàng)建多邊形:

但是,通過這種方式創(chuàng)建的多邊形中,彈幕以角度為單位平均分布。而有時,我們希望達到的是下圖效果:

此時,我們就需要通過創(chuàng)建均分直線來實現(xiàn)目的
1.均分線段

假設有一條線段,其上可以分布有任意數(shù)量的點,其中,線段兩端分別記為A0與A1,隨著線段上的點由A0向A1靠近,An的下標“勻速”增大,那么要如何確定An的坐標?

總而言之,假設A0的坐標為(x0,y0),A1的坐標為(x1,y1),那么可以通過以下代碼確定An的坐標:
xn=n*x1+(1-n)*x0
yn=n*y1+(1-n)*y0
通過這一形式,我們可以創(chuàng)建一條均分直線。比如,若我們要將一條線段平均分為十段,每段的端點上有一個彈幕,我們可以通過如下的代碼形式實現(xiàn):
for(i=0;i<=10;i+=1){
????n=i/10
????xn=n*x1+(1-n)*x0
????yn=n*y1+(1-n)*y0
????instance_create(xn,yn,obj)
}
2.多邊形
為圖簡便,我們將上述的創(chuàng)建均分直線的代碼改寫為腳本

又因為,一個正多邊形的邊可以由圓上平均分布的數(shù)點確定

因此,創(chuàng)建多邊形的代碼可以寫作類似如下形式:
R=150
dir=15
n=5
Nums=6
for(i=0;i<n;i+=1){
? ? xA=lengthdir_x(R,360*i/n+dir)
? ? yA=lengthdir_y(R,360*i/n+dir)
? ? xB=lengthdir_x(R,360*(i+1)/n+dir)
? ? yB=lengthdir_y(R,360*(i+1)/n+dir)
????Line(xA,yA,xB,yB,Nums)
}
同樣的,除了多邊形以外,通過參數(shù)方程確定某些點,再連接這些點,可以生成眾多不同的圖像。
下列圖形留作習題:


二.擴散
假設我們想要創(chuàng)建一個從點向外擴散的圓,顯然可以通過以下代碼實現(xiàn):
for(i=0;i<60;i+=1){
????inst=instance_create(400,304,obj)
????inst.direction=i*6
????inst.speed=3
}
但是,如果換作正方形等各種不規(guī)則圖形,要怎么確定速度呢?
可以證明,當所有彈幕由同一點勻速射出時,它的形狀將與速度矢量所形成的形狀保持相似
因此,假設我們要創(chuàng)建一個擴散的多邊形彈幕,可以通過更改上文所述的創(chuàng)建多邊形代碼實現(xiàn):
R=10
dir=15
n=5
Nums=6
for(i=0;i<n;i+=1){
? ? xA=lengthdir_x(R,360*i/n+dir)
? ? yA=lengthdir_y(R,360*i/n+dir)
? ? xB=lengthdir_x(R,360*(i+1)/n+dir)
? ? yB=lengthdir_y(R,360*(i+1)/n+dir)
????for(i=0;i<=10;i+=1){
????????n=i/10
????????xn=n*xB+(1-n)*xA
????????yn=n*yB+(1-n)*yA
????????inst=instance_create(400,304,obj)
????????inst.direction=point_direction(0,0,xn,yn)
????????inst.speed=point_direction(0,0,xn,yn)
????}
}
三.正則變換與圓形區(qū)域隨機彈
若我們要在一個半徑為200的圓內(nèi)創(chuàng)建200個隨機彈,顯然我們很容易想到如下方式:
repeat(200){
? ? Dir=random(360)
? ? Len=random(200)
? ? instance_create(320+lengthdir_x(Len,Dir),240+lengthdir_y(Len,Dir),objCherry)
}

這種方式創(chuàng)建出的彈幕如圖所示。
然而,由于這種方法創(chuàng)建出的彈幕,圓心位置的彈幕會比周圍更加密集。在某些特定情況下(比如kid有可能位于圓心)會產(chǎn)生一些不合理的配置,有沒有辦法讓所有位置的彈幕平均分布呢?
首先,我們將Dir與Len視作直角坐標系上的隨機變量,如下圖所示,顯然,Dir與Len將在區(qū)域內(nèi)平均分布,因此,在每塊小正方形區(qū)域內(nèi)的彈幕數(shù)量可視作相等

之后,我們將其投影到極坐標上,顯然,如果要使彈幕保持平均分布不變,則每塊投影區(qū)域的面積相等

作正則變換,有

將結(jié)果進一步簡化,可得以下代碼:
repeat(200){
? ? Dir=random(360)
? ? Len=200*sqrt(random(1))
? ? instance_create(320+lengthdir_x(Len,Dir),240+lengthdir_y(Len,Dir),objCherry)
}
結(jié)果如下:

此外,若對參數(shù)稍加改動,還可能出現(xiàn)如下結(jié)果

Len=200*power(random(1),0.25) :中間稀,外部密
Len=200*sqrt(random(1)+0.5) :均勻分布圓環(huán)
Len=200*power(random(1),4) :內(nèi)部密,外部稀
四.圖形過渡與sigmoid函數(shù)
在上文中,我們提到了確定線段上An坐標的方法,那么,假設有兩個圖形,我想要讓彈幕從圖形1變?yōu)閳D形2,是否也可以用這種方法實現(xiàn)?
事實上,假設圖形S0上每個點(xi,yi)都能對應到另一個圖形S1上的點(x'i,y'i),那么就可以通過x‘’i=n*x'i+(1-n)*xi,y‘’i=n*y'i+(1-n)*yi的方式實現(xiàn)
例如:
Time+=1
if Time=1{
????for(i=0;i<60;i+=1){
????? ? inst[i]=instance_create(200+lengthdir_x(100,i*6),304+lengthdir_y(100,i*6),obj)
????}
}
if Time>=40 && Time<=50{
????n=(Time-40)/10
? ? for(i=0;i<60;i+=1){
????????x1=200+lengthdir_x(100,i*6)
????????y1=304+lengthdir_y(100,i*6)
? ? ? ? x2=600+lengthdir_x(100,90-i*6)
????????y2=304+lengthdir_y(100,90-i*6)
????? ? inst[i].x=n*x2+(1-n)*x1
????????inst[i].y=n*y2+(1-n)*y1
????}
}
這樣,彈幕就能從一個圓變換成另一個圓
不過,這種變換并不平滑,為使變換更為流暢,我們可以將n用sigmoid函數(shù)來表示

如:
Time+=1
if Time=1{
????for(i=0;i<60;i+=1){
????? ? inst[i]=instance_create(200+lengthdir_x(100,i*6),304+lengthdir_y(100,i*6),obj)
????}
}
if Time>=40 && Time<=80{
????n=1/(1+exp(-(Time-60)/2))
? ? for(i=0;i<60;i+=1){
????????x1=200+lengthdir_x(100,i*6)
????????y1=304+lengthdir_y(100,i*6)
? ? ? ? x2=600+lengthdir_x(100,90-i*6)
????????y2=304+lengthdir_y(100,90-i*6)
????? ? inst[i].x=n*x2+(1-n)*x1
????????inst[i].y=n*y2+(1-n)*y1
????}
}
這種方式不僅可以在靜態(tài)圖形間進行變換,也可以在動態(tài)彈幕間進行變換,只需要彈幕的坐標變化可以通過代碼來表示,例如:
Time+=1
if Time=1{
????for(i=0;i<60;i+=1){
????? ? inst[i]=instance_create(200,304,obj)
????????spe[i]=random_range(10,20)
????????dir[i]=random(360)
????????x1[i]=200
????????y1[i]=304
????}
}
for(i=0;i<60;i+=1){
????x1[i]+=lengthdir_x(spe[i],dir[i])
????y1[i]+=lengthdir_y(spe[i],dir[i])
}
if Time<40{
? ? for(i=0;i<60;i+=1){
? ? ? ? inst[i].x=x1[i]
? ? ? ? inst[i].y=y1[i]
? ? }
}
if Time>=40?&& Time<=80{
????n=1/(1+exp(-(Time-60)/2))
? ? for(i=0;i<60;i+=1){
? ? ? ? x2=600+lengthdir_x(100,90-i*6)
????????y2=304+lengthdir_y(100,90-i*6)
????? ? inst[i].x=n*x2+(1-n)*x1[i]
????????inst[i].y=n*y2+(1-n)*y1[i]
????}
}
其中,黑體部分是模擬隨機彈的x,y坐標變化的代碼
Time分成<40和40<<80兩種條件是為了防止e的次方過大導致數(shù)據(jù)溢出

附.一些常見的簡單彈幕

for(i=0;i<=120;i+=1){
? ? inst=instance_create(i*10-200,304,objCherry)
? ? inst.direction=i*10
? ? inst.speed=1
}
即:角度隨著與直線端點的距離增大而增大

for(i=0;i<=60;i+=1){
? ? dir=i*6
????inst=instance_create(400+lengthdir_x(100,dir),304+lengthdir_y(100,dir),objCherry)
? ? inst.direction=-i*6
? ? inst.speed=1
}
即:速度角度與速度角度改變量相反

friction=0.5
hspeed+=lengthdir_x(1,point_direction(x,y,mouse_x,mouse_y))
vspeed+=lengthdir_y(1,point_direction(x,y,mouse_x,mouse_y))
將mouse_x,mouse_y改成跟蹤對象的x,y坐標即可食用

若還有其他可能比較有意思的彈幕,可能會在教程(5.X)中補充,也歡迎在評論區(qū)或者加入我的群提問,群號726054484。
下一章將簡要講一講關于GML中繪制函數(shù)的一些應用(不包括表面)