最近有小夥伴問我瞄準線遇到各類形狀該怎麼處理?如何實現反覆橫跳的瞄準線?最近恰好在《Cocos Creator遊戲開發實戰》中看到物理系統有一個射線檢測,因而,基於這個射線檢測,寫了一個反覆橫跳的瞄準線效果。一塊兒往下看吧!文章底部獲取完整項目!
國際慣例,先上最終效果!node
在講解以前咱們須要一些向量的知識,簡單的介紹一些吧!git
向量的加法,OA + AB = OB
github
向量的點乘,表示一個向量在另外一個向量上的投影,是個標量,有正負之分。向量夾角小於 90度 爲正數,等於 90度 爲 零,大於 90度 爲負數。segmentfault
向量的叉乘,結果爲向量,正好垂直於兩個向量構成的平面(右手系),也稱爲法向量。這裏暫時沒用到,順便提一下。編輯器
接下來進入正題,已知入射向量(單位向量),法向量(單位向量),如何得出反射向量?測試
咱們將反射向量平移至入射向量起點,延長法向量與其相交,這個延長線的長度,恰好是 入射向量在法向量上的投影的相反數的兩倍 。再根據投影和向量加法能夠推出反射向量的計算公式。this
清楚了麼?不清楚也不要緊,記得最後的公式就能夠了,接下來進入 cocos creator 操做環節。spa
既然是物理系統中的碰撞檢測,咱們在編輯器裏添加的是物理系統中的碰撞器,而不是引擎的碰撞器,不要選錯了哦。3d
不動的剛體類型設爲 static
,添加完全部的物理碰撞器後以下所示。code
用到物理引擎天然要把物理引擎打開。
cc.director.getPhysicsManager().enabled = true;
如何進行射線檢測的?經過起始點、入射方向和剩餘線段的長度獲取射線檢測的結果。若是檢測到碰撞體,就畫入射線段,並計算反射方向,再次進行射線檢測;若是未檢測到碰撞體,就把剩餘線段畫完。主要代碼以下:
/** * @description 計算射線 * @param startLocation 起始位置 世界座標系 * @param vector_dir 單位方向向量 */ private drawRayCast(startLocation: cc.Vec2, vector_dir: cc.Vec2) { // 剩餘長度 const left_length = AIM_LINE_MAX_LENGTH - this._cur_length; if (left_length <= 0) return; // 計算線的終點位置 const endLocation = startLocation.add(vector_dir.mul(left_length)); // 射線測試 const results = cc.director.getPhysicsManager().rayCast(startLocation, endLocation, cc.RayCastType.Closest); if (results.length > 0) { const result = results[0]; // 指定射線與穿過的碰撞體在哪一點相交。 const point = result.point; // 畫入射線段 this.drawAimLine(startLocation, point); // 計算長度 const line_length = point.sub(startLocation).mag(); // 計算已畫長度 this._cur_length += line_length; // 指定碰撞體在相交點的表面的法線單位向量。 const vector_n = result.normal; // 入射單位向量 const vector_i = vector_dir; // 反射單位向量 const vector_r = vector_i.sub(vector_n.mul(2 * vector_i.dot(vector_n))); // 接着計算下一段 this.drawRayCast(point, vector_r); } else { // 畫剩餘線段 this.drawAimLine(startLocation, endLocation); } }
如何畫瞄準線小圈圈?經過結束位置和起始位置計算數量和間隔向量,畫出一個個小圓圈。參考代碼以下。
/** * @description 畫瞄準線 * @param startLocation 起始位置 世界座標系 * @param endLocation 結束位置 世界座標系 */ private drawAimLine(startLocation: cc.Vec2, endLocation: cc.Vec2) { // 轉換座標 const graphic_startLocation = this.graphic_line.node.convertToNodeSpaceAR(startLocation); this.graphic_line.moveTo(graphic_startLocation.x, graphic_startLocation.y); // 畫小圓圓 // 間隔 const delta = 20; // 方向 const vector_dir = endLocation.sub(startLocation); // 數量 const total_count = Math.round(vector_dir.mag() / delta); // 每次間隔向量 vector_dir.normalizeSelf().mulSelf(delta); for (let index = 0; index < total_count; index++) { graphic_startLocation.addSelf(vector_dir) this.graphic_line.circle(graphic_startLocation.x, graphic_startLocation.y, 2); } }