在開始前先聲明,我並沒有受過正規的三角函數教學,我對它的認識都是出於實做的練習還有自行的理解而來,因此可能有不正確的地方,敬請指教。

我很喜歡寫遊戲,從一開始用scratch,到後來用python,寫遊戲一直都是我的興趣之一。在python寫遊戲沒有scratch內建的一些功能,例如 移動X步、到XX的距離、碰撞檢測等等,所以這些都是我自行設計的,而像是移動,假如說要往某個方向移動,就必須使用三角函數,這就是我最初使用的原因。以移動來說,只要用sin和cos分別對應y和x就可以了。寫起來會像是這樣:

x = cos(angle)

y = sin(angle)

這部份沒什麼特別的,但如果是殭屍,要不停等速向玩家移動,該怎麼做呢?我們擁有的資訊有殭屍和玩家的座標,唯一缺乏的就是殭屍與玩家的角度。角度?如果沒有角度,那就算有上面那個移動算式那又如何,還是沒辦法往正確的方向移動啊!但是要怎麼從座標轉換成角度?

我以前用scratch做過一個專案「機關王」,裡頭是模擬一個物理世界,讓玩家排列軌道,讓球在上面滑行,當時也面臨一樣的問題,球現在移動的方向是什麼?由於球是用x y 的加速度來移動的,所以並不能直接取得角度,當時我也是和我爸討論很久,他就丟給我一個算式:

atan(y/x)

用這個算出來之後,球正確的顯示目前的方向了,但其實實際上我仍然不知道它原理是什麼。

所以當我遇到求角度的問題時,我直覺就想到之前做的「機關王」,於是我去把它翻出來看程式碼,然後把那行算式抄進程式裡,先讓殭屍算出面向玩家的角度,再讓殭屍朝這個角度前進。一開始運行都還沒問題,殭屍很正常的追過來,但是當我走到它的左手邊時,它竟然反而遠離我了!我在它身邊一直實驗,發現只要我在它的右邊(x 大於殭屍),它就會靠近我,而只要在左邊(x小於殭屍)它就會遠離我,這很奇怪,因為我在scratch沒有遇到這個問題,更怪的是,y軸的移動竟然沒有影響!如果是因為正負值的關係,照理說在殭屍的四個象限應該結果都會不一樣,但是卻只有改變x座標時會被影響。


此圖片為概念圖,中間紫色圓圈為殭屍,三角箭頭為殭屍的方向,橘色圓圈為玩家站的位置。

以此圖片可以發現當無論玩家在哪一個象限,殭屍的角度都會是正的,假如說x是負的,那角度就會離正確的差半圈。

半圈就半圈,會難倒我嗎?就寫個if判斷式,如果x為負就將角度再加上180,不就好了嘛!但是當我照這樣做,怪物反而朝向怪怪的角度,說怪是什麼意思,就是完全看不出有什麼關聯性的角度。除了這個我還把atan(x/y)、tan(y/x)、tan(x/y)、atan(y/abs(x))什麼的全部都試過了,但出來的角度是一個比一個怪(abs為絕對值),最接近的就是上面的atan(y/x)了。

看起來是解不開,我突然想到過去曾經用的另外一的方法,之前也是在做scratch時,我曾經做過一個「我是神射手」的遊戲,裡面類似憤怒鳥,只是改成在投籃,但當時我製作投球的地方,並沒有使用任何三角函數,而是使用x和y的比例。什麼意思?假如我們從(0,0)移動到(3,4),那我們移動的距離就是用畢氏定理:(3^2 +4^2) ^ 0.5(^代表次方,^0.5就是開根號),那如果我們放慢速度,用十次的時間從(0,0)移動到(3,4),該怎麼做呢?我們可以把3/10、4/10,每次移動(0.3,0.4),移動了十次就可以到(3,4),那假如不知道總共要移動幾次該怎麼辦?時間=距離/速度,那距離就是用畢氏定理算,我們設為distance,速度就是殭屍的速度,設為speed,所以x要移動的距離就是x / [(x^2+y^2)^0.5 / speed],整理一下,就會發現是speed * x / distance,那y就是speed * y / distance,x / distance,distance就是斜邊,發現了嗎?我們正在算三角函數!X / distance不就是臨邊/斜邊也就是cos嗎?同理,y / distance就是對邊/斜邊也就是sin,我們還是在用三角函數,只是不是用角度去算,而是從一個真實三角形下去計算。其實可以用另外一個角度去理解,我們總共要移動distance的距離,那其中x的速度就是x / distance,y就要移動 y / distance,就是x和y在總距離之中的比例。將這個比例乘上速度就能夠照我們想要的速度去移動了。

悟出這個道理解開我長久以來的疑惑,為什麼我不用三角函數會跟用了三角函數有相同的結果?我們再回到最前面,為什麼我們要乘上cos(angle),cos是得到臨邊/斜邊的比例,我們將這個比例乘上速度就可以讓它朝向一個「角度」前進,那我們speed * x / distance其實也是相同的概念,只是這個比例是我們依據實際的情況計算出來的,但其實是一樣的東西,只是方法不一樣。

那這樣殭屍就能朝著玩家前進了。但是過了不久,我開始寫一隻新的怪物—狙擊手,狙擊手會將槍一直面向玩家,時而發射子彈,問題又來了,面向玩家的角度是多少?而且這次更棘手,因為目的不是要移動,而是槍確確實實的要朝著玩家,這可不能用剛才那個方法求比例了,我先再試了前方提到的atan(y/x),跟之前的經驗相同,槍只會有在玩家在右邊時才會正常,左邊就會朝向反方向。看來前面提到的問題非解決不可了,跟上次的不同,我改成將算出角度乘上-1,發現槍就像是鏡子一般,變成左右相反,這樣證明了在中間時角度會是0,雖然如此,但左右相反還是不對,於是我就開始亂試,+180、+360、+120、+150、+270...欸+270結果竟然完全沒變!意思是當我+270正好繞了一圈,所以只要將角度加上135就正常了,但這太詭異了,為什麼是270而不是360或者是180?難道三角函數求出的圓是270度?

這實在太詭異了,為了觀察到底繞一圈是幾度,玩家的槍也是繞圈圈的,我將玩家槍的角度印出來,觀察繞一圈是在幾度的時候,但結果嚇壞我了,本來預期是180或是360為一個單位,但竟然才6出頭就繞一圈了!我把這個發現跟我姐討論,她突然想到可能單位不是角度,而是弧度。在此之前我不知道弧度是什麼,上網查後知道它是計算角度的另外一種單位,又稱為徑度,主要的概念是當一個扇形半徑等於圓弧時的角度就是一弧度,我自己算了一下,半徑為r的時候圓周就是2 pi r,而扇形的圓弧長就是將圓周乘以比例,那我們設一弧度好了,設一整個圓的弧度為x,2 pi r * 1 / x = r,那x就是2 pi,所以說一個圓是2 pi弧度。

說了老半天,到底這跟三角函數有什麼關係?原來三角函數所使用的角度單位就是弧度!而不是360一圈的度。這就是為什麼加上180不對了,但為什麼270沒錯?說明確點,為什麼270會剛好繞一圈?既然如此,不然我們就來算算看270弧度究竟繞了圓幾圈,一個圓是2 pi弧度,那這樣270 / 2 pi可以知道繞了多少圈圓了,使用python當計算機實際算,得出的結果是42.97183463481174,42.97,距離完整一個圓只差0.03而已,所以它並非剛好是一個圓,只是由於很接近所以肉眼看不出差別罷了。


那答案很清楚了,要讓槍朝向正確的方向,只要在x小於零也就是人在怪物左邊的時候,將得出的角度加上pi就可以了,實際測試也的確沒錯。

 

在最後提出幾個我自己想不通的問題:

1.為什麼atan可以求出角度,還有為什麼這個角度只能是正的?

2.三角函數的內部究竟是怎麼算出來的?

如果你知道答案,請在文章下方留言,我很希望你告訴我,丟個連結也行。如果不知道的話,也可以一起想想喔!

 

剛才所說的那些都是我一個正在開發的遊戲所遇到的狀況,連結在此:https://github.com/lancatlin/FireWheel

機關王:https://scratch.mit.edu/projects/137788804/

我是神射手:https://scratch.mit.edu/projects/113011585/

歡迎去逛逛

 

文章標籤
全站熱搜
創作者介紹
創作者 Cat Lan 的頭像
Cat Lan

Lancat Server 懶貓伺服器

Cat Lan 發表在 痞客邦 留言(0) 人氣(48)