1. 程式人生 > >Unity學習筆記(3)-----製作一個模擬星系(複雜版)【Step1】

Unity學習筆記(3)-----製作一個模擬星系(複雜版)【Step1】

繼續學習Unity3D  這周任務之一是要做一個太陽系,如下圖:


而這次筆記的重點不在作業上~.~, 而是在於如何建立一個, 真正意義上的  “星系”。

上過課都知道, 作業簡單用以下兩條語句即可搞定:

Planet.gameObject.transform.RotateAround();
Planet.gameObject.transform.Rotate();

 然而這樣做並沒有實現真的模擬, 因為:

1. 軌道只能是圓的..... 而大部分星球軌道不可能完全是圓, 只是有些軌道離心率略低看起來像是圓罷了。。。

2. 憑感覺(因為高中物理忘得差不多了), 這樣弄出來的各個星球的轉速放在同一個中心天體的星系中可能會違反物理規律.....

3. 用的物理模型本身就不符 ~。~  Rotate只有旋轉而已, 不能體現各個星球之間對各自軌道的影響

------------------------------------裝逼與正文分割線-----------------------------------------

所以第一個問題, 就是如何建立一個引力場。

我們知道每個星球都有引力, 而且引力範圍是無限的。各個天體之間的引力由萬有引力公式給出

那麼, 首先我們新建一個 Sphere 物體, Reset其位置, 當做中心天體。

然後, 給這個物體新增Rigidbody屬性, 這是為了給此物件新增物理事件, 比如我們需要的引力, 碰撞等......

將其做成一個預設, 放在Resources/Prefabs 中。(資料夾名字一定要是Resources裡啊, 不然之後沒法載入...)

當然儲存Scene也是習慣了, 還有一些瑣碎操作, 什麼調攝像機鏡頭到合適角度啊,設定物體Scale和Mass啊之類的, 合理就行。

放一個我設定的圖:(Rotation那裡忽略, 設定為0就好! 這圖是我在模擬過程中隨手截的, 等下會解釋這個)


然後就是新增一個Script指令碼, 實現引力效果。

要實現引力, 大致可以分為以下幾個步驟:

  1. 設定一個引力範圍。
  2. 選定在引力範圍內的所有天體。
  3. 給每個天體都加一個來自此物件的力(即指向此物件), 力的大小由萬有引力公式決定。
對應程式碼如下:
	void KeepUniversalG() {
		var Center = this.transform.position;
		Collider[] colliders = Physics.OverlapSphere (Center, GalaxyRange);
		foreach (Collider obj in colliders) {
			Rigidbody rb = obj.GetComponent<Rigidbody> ();
			if (rb != null)
				rb.AddExplosionForce (-1 * UniversalG(this.gameObject, obj.gameObject) * Time.deltaTime, Center, GalaxyRange);
		}

	}
沒高亮?.... 好傷啊。 API就不多解釋了, 大概從名字和引數都看得懂是幹啥。 就說一句:
AddExplosionForce(...);
這個從名字看是實現從某物體發出的產生爆炸力的作用效果, 但是注意在引數設定Force那裡*-1, 改變力的方向不就是引力了麼(~。~) PS: 開始沒注意說明: GalaxyRange是類的一個private float, 自己設定引數值就好, 表示的是星系引力作用範圍。  UniversalG是計算力的大小的函式。 等下具體解釋, 先直接建一個函式在裡面寫return 某個值就好(先看效果嘛)。 然後把這函式放進FixedUpdate裡就行了。 現在你在地圖上中心天梯附近隨便建幾個Sphere, 能看到明顯的引力效果。(建議用中心天體的Prefab, 不用自己一個個加Rigidbody和掛載這個指令碼) 接下來要考慮的就是怎麼讓它們繞著中心天體轉而不是直接黏上去了。 ----------------------------------高中物理勢力登場---------------------------- 物體 之所以會繞著天體旋轉, 一是有一個合適初始速度, 二是受到一個始終與速度方向垂直(或近似垂直)的力。 引力為我們解決了第二點, 現在只要解決第一點即可。 而之後我們會看到, 這第一點其實並不容易。 它對於建立一個能夠維持穩定的星系具有很重要的影響。 然而我們先從最簡單的開始。 由於要做較多的簡化, 所以這次直接先貼程式碼了...
	void Start () {
		var Center = this.transform.position;
		InitialForce = ProperForce (this.gameObject);
		this.gameObject.GetComponent<Rigidbody> ().AddForce (InitialForce * ProperDirect(Center));
	}
複雜就複雜在ProperForce和ProperDirect兩函式得寫出來。 這個之後再管什麼“proper”, 首先我們要的是效果, 效果! 所以。。。Force值隨便自己建一個函式設一個return值就好,  ProperDirect只要保證兩點就可以: 1. 當傳入引數為(0,0,0)時(即中心天體位置), 返回的也是Vecter3.zero。 2. 對於其他傳入引數, 保證返回的向量與傳入的向量方向垂直。即 存在關係:
Center * ProperDirect(Center) == 0 //這裡指向量點乘。

以上工作做完, 儲存指令碼, 看看效果。 

這時候的效果應該不是太好, 原因如下:

1.引數亂設的。 2. 天體不美觀。 3.軌道很難觀察  4. 可能還有Bug、、、(這是最氣的)

鑑於寫到這裡我發現還有很多沒寫,, 我還是另寫一篇做下文吧, 不然太長了。

先發下成品效果圖。。

懶得發動圖了, 角度也懶得換了。。。 反正自己看著還挺不錯。