遊戲中戰鬥傷害範圍-彈道飛行
回顧前瞻
在上一篇文章《遊戲中戰鬥傷害範圍攻擊計算完整全版》我們計算了扇形,多邊形,圓形,等傷害範圍獲取。
但是前天的多邊形規整計算中,我發現一個問題,就是在獲取多邊形判斷的時候,總有驗證不足的情況,也就是未包含出現!
最後百度幾何原理,得到一個演算法
1 /*我們可以把多邊形可以看做是一條從某點出發的閉合路,可以觀察到在內部的點永遠都在路的同一邊。 2 給定線段的兩個點P0(x0,y0)和P1(x1,y1),目標點P(x,y),它們有如下的關係: 3 計算(y - y0) (x1 - x0) - (x - x0) (y1 - y0) 4 如果答案小於0則說明P線上段的右邊,大於0則在左邊,等於0說明線上段上。5 */
更簡單便捷的方式,支援浮點數計算:
1 /** 2 * 驗證點在多邊形內 3 * 4 * @param x 5 * @param z 6 * @return 7 */ 8 public boolean contains(double x, double z) { 9 /*我們可以把多邊形可以看做是一條從某點出發的閉合路,可以觀察到在內部的點永遠都在路的同一邊。 10 給定線段的兩個點P0(x0,y0)和P1(x1,y1),目標點P(x,y),它們有如下的關係:11 計算(y - y0) (x1 - x0) - (x - x0) (y1 - y0) 12 如果答案小於0則說明P線上段的右邊,大於0則在左邊,等於0說明線上段上。 13 */ 14 double p1x = 0, p1z = 0, p2x = 0, p2z = 0, ret = 0; 15 for (int i = 0; i < pointCount; i++) { 16 p1x = pointXs[i]; 17 p1z = pointZs[i]; 18 if(i == pointCount - 1) { 19 p2x = pointXs[0]; 20 p2z = pointZs[0]; 21 } else { 22 p2x = pointXs[i + 1]; 23 p2z = pointZs[i + 1]; 24 } 25 double ss = sq(p1x, p1z, p2x, p2z, x, z); 26 if (ss != 0) { 27 /*答案小於0則說明P線上段的右邊,大於0則在左邊,等於0說明線上段上。*/ 28 if (ret != 0) { 29 /*如果不是0,表示方向反向了*/ 30 if ((ss > 0 && ret < 0) || (ss < 0 && ret > 0)) { 31 return false; 32 } 33 } 34 ret = ss; 35 } 36 } 37 return true; 38 } 39 40 double sq(double p1x, double p1z, double p2x, double p2z, double x, double z) { 41 return (z - p1z) * (p2x - p1x) - (x - p1x) * (p2z - p1z); 42 }
再次驗證測試
1 public static void main(String[] args) { 2 double px = 2.901; 3 double py = 3.001; 4 PolygonCheck polygonCheck = new PolygonCheck(4); 5 polygonCheck.add(2, 2); 6 polygonCheck.add(3, 2); 7 polygonCheck.add(3, 5); 8 polygonCheck.add(2, 5); 9 System.out.println(polygonCheck.contains(3, 5.001)); 10 System.out.println(polygonCheck.contains(3, 5)); 11 System.out.println(polygonCheck.contains(2.5, 4)); 12 }
結果
1 --- exec-maven-plugin:1.2.1:exec (default-cli) @ net.sz.game.engine --- 2 false 3 true 4 true 5 ------------------------------------------------------------------------
這時修正過後的驗證演算法,具體實現請把函式複製到上一篇《遊戲中戰鬥傷害範圍攻擊計算完整全版》文章 PolygonCheck 類中就ok了
====================================華麗麗分割線==================================
彈道飛行前奏
劃分彈道飛行:
1,普通彈道飛行,計算飛行距離鎖定目標傷害;
2,真實的彈道飛行,計算飛行碰撞目標傷害;
第二類:
2.1:飛行彈道是多邊形,直線飛行;
2.2:飛行彈道是扇形,直線飛行;
2.3:飛行彈道是圓形,直線飛行;
當然上面提到的都可以是曲線,賽爾線,等飛行情況;
但是都是非常複雜的飛行方式。可以一步一步參考修改;
普通彈道飛行計算
最簡單的計演算法則規則是:策劃配置彈道飛行速度(S)飛行的距離(R);計算飛行時間;時間(T)
普通彈道飛行,只計算飛行時間,得到延遲傷害計算:
T=R*1000/S;得到基本飛行時間,加入傷害定時器,延遲處理就行了,法師的普通傷害從技能放出,特效飛行開始延遲T計算技能傷害;
當然這裡的T應該還要加上從釋放技能開始動作釋放時間,T+SKill_T(技能動作持續釋放時間,以後才是彈道飛行);才是真正的技能彈道飛行時間;
範圍彈道飛行計算
彈道飛行必須處理好,技能攻擊物件數量總和,以及已經攻擊過的物件id集合儲存;
範圍傷害彈道飛行;
圓形的飛行彈道範圍計算,其實也是最簡單的,就是模擬移動位置就是了;
只需要改變原點A的位置就可以了;每一次得到攻擊物件,都判斷點位到A的距離是否小於等於R;
彈道飛行復雜計算
彈道飛行處理中;
扇形計算,扇形彈道飛行,原點是不變化位置,只計算飛行R,R1(V1扇形),R2(V2),每一次飛攻擊範圍為R2-R1(V1到V2之間);知道飛行R2大於R,
多邊形計算彈道飛行的時候必須要移動多邊形,那就是要移動多邊形的中心點才能得到新的多邊形;
多邊形和扇形的計算公式和注意事項,下面會有點展示;
PS,第一次移動的多邊形外頂點(C,D)是下一次多邊形的內定的(B,A)的起始點;
必須要記錄彈道飛行時間,處理時間,飛行速度,飛行距離和上一次飛行距離;
/*飛行的總距離 除以 飛行時間,得到每一單位時間飛行的距離,單位時間ms*/
傷害定時器測試類
1 package net.sz.tmp; 2 3 import java.util.HashSet; 4 import net.sz.game.engine.navmesh.Vector3; 5 import net.sz.game.engine.struct.PolygonCheck; 6 import net.sz.game.engine.struct.Vector; 7 import net.sz.game.engine.thread.TimerTaskEvent; 8 import net.sz.game.engine.utils.MoveUtil; 9 import org.apache.log4j.Logger; 10 11 /** 12 * 傷害計算定時器 13 * <br> 14 * author 失足程式設計師<br> 15 * mail [email protected]<br> 16 * phone 13882122019<br> 17 */ 18 public class HitTimer extends TimerTaskEvent { 19 20 private static final Logger log = Logger.getLogger(HitTimer.class); 21 /*飛行速度 每一毫秒*/ 22 private float flySpeedMS = 0; 23 /*上一次飛行距離 計算*/ 24 private float upFlyDis = 0f; 25 /*最後一次飛行距離 計算*/ 26 private float lastFlyDis = 0f; 27 /*最後一次飛行時間計算時間差 計算*/ 28 private long lastFlyTime = 50; 29 private int attackMAX = 5; 30 private HashSet<Long> attackIds = null; 31 /*hittimer 執行間隔時間*/ 32 private int[] flyArray; 33 /*定時器執行間隔時間迴圈次數*/ 34 private int flyCount = 0; 35 /*測試預設飛行距離都是5米*/ 36 private float limit2 = 5; 37 /*目標中心點*/ 38 private Vector3 center; 39 /*當前朝向*/ 40 private Vector centerdir; 41 42 public HitTimer(Vector3 center, Vector centerdir, int actionCount, int forCount, boolean removeZore, float flyspeed, int[] intervalTime) { 43 super(actionCount, intervalTime[0]); 44 45 this.flyCount = forCount; 46 /*飛行的總距離 除以 飛行時間,得到每一單位時間飛行的距離,單位時間ms*/ 47 this.flySpeedMS = limit2 / (limit2 * 1000f / flyspeed); 48 this.center = center; 49 this.centerdir = centerdir; 50 51 if (removeZore) { 52 /* 53 為什麼要除去第0個引數,是因為在初始化傷害計算公式的時候技能動作需要釋放時間 54 然後才是彈道飛行時間,當然也不是所有技能都是如此 55 */ 56 flyArray = new int[intervalTime.length - 1]; 57 for (int i = 1; i < intervalTime.length; i++) { 58 flyArray[i - 1] = intervalTime[i]; 59 } 60 } else { 61 flyArray = intervalTime; 62 } 63 } 64 65 /*我這裡只寫彈道飛行程式碼*/ 66 @Override 67 public void run() { 68 69 /*把最後一次移動距離賦值給上一次移動距離*/ 70 this.upFlyDis = this.lastFlyDis; 71 /*計算時間差*/ 72 long tmptime = lastFlyTime; 73 if (lastFlyTime > 20000) { 74 tmptime = System.currentTimeMillis() - lastFlyTime; 75 } 76 /* 這裡拿到間隔時間應該位移偏移量 */ 77 float mvr = tmptime * flySpeedMS; 78 /*彈道累計飛行距離*/ 79 this.lastFlyDis += mvr; 80 if (this.lastFlyDis >= limit2) { 81 this.lastFlyDis = limit2; 82 /*累計飛行距離已經超過彈道距離*/ 83 this.setCancel(true); 84 } 85 86 /*計算本次剩餘能攻擊物件數量*/ 87 int qtarget_max = attackMAX; 88 if (this.attackIds != null) { 89 qtarget_max -= this.attackIds.size(); 90 if (qtarget_max <= 0) { 91 /*需要取消該計算公式*/ 92 this.setCancel(true); 93 return; 94 } 95 } 96 /*獲取戰鬥物件*/ 97 { 98 /*例如待驗證的物件座標點*/ 99 double px = 5.2, pz = 6.3; 100 101 { 102 /*扇形*/ 103 double atan360 = 0; 104 /*扇形的邊*/ 105 double aTan360_A1 = 0; 106 /*扇形的邊*/ 107 double aTan360_A2 = 0; 108 /*預設是60度夾角的扇形 左右偏移30°*/ 109 double skillAngle = 30; 110 /*有角度 為扇形*/ 111 atan360 = centerdir.getAtan360(); 112 aTan360_A1 = MoveUtil.getATan360(atan360, -1 * skillAngle); 113 aTan360_A2 = MoveUtil.getATan360(atan360, skillAngle); 114 /*如果整個扇形有偏移設定 115 if (skillDir != 0) { 116 aTan360_A1 = MoveUtil.getATan360(aTan360_A1, skillDir); 117 aTan360_A2 = MoveUtil.getATan360(aTan360_A2, skillDir); 118 } 119 */ 120 { 121 /* 122 在獲取場景物件的時候判斷距離由於多邊形和圓形存在切面夾角問題 123 所有在獲取場景物件的時候放寬2碼 124 125 範圍攻擊 126 getFighters(this.center, this.centerVr, type, skillModel.getQrange_limit2(), this.upFlyDis, this.lastFlyDis, this.attackIds, 127 this.attacker, this.target, fighters, qtarget_max, skillModel.getQangle(), skillModel.getQdir()); 128 得到所有可攻擊物件, 129 */ 130 /*獲取當前點和目標點 朝向*/ 131 double tmpTan360 = MoveUtil.getATan360(center.getX(), center.getZ(), px, pz); 132 if ((aTan360_A1 > aTan360_A2 && ((aTan360_A1 <= tmpTan360 && tmpTan360 <= 360) || (0 <= tmpTan360 && tmpTan360 <= aTan360_A2))) 133 || (aTan360_A1 > aTan360_A2 && aTan360_A1 <= tmpTan360 && tmpTan360 <= aTan360_A2)) { 134 /*"修正後的夾角:" + aTan360_1 + " ~ 360 和 0 ~" + aTan360_2*/ 135 double distance = MoveUtil.distance(center.getX(), center.getZ(), px, pz); 136 if (!(upFlyDis < distance && distance <= lastFlyDis)) { 137 /* 138 改物件不在攻擊扇形範圍內 139 return; 140 */ 141 } else { 142 /* 143 記得新增本次可攻擊物件到集合 144 this.attackIds.add(0L); 145 */ 146 } 147 } else { 148 /* 149 改物件不在攻擊扇形範圍內 150 return; 151 */ 152 } 153 } 154 } 155 { 156 /*多邊形*/ 157 Float qskillwidth = 3f; //矩形寬度 158 Float qskillhight = 4f; //矩形長度 159 int qoffset = 0; //矩形原點修正值 160 161 if (lastFlyDis > 0) { 162 qoffset += upFlyDis; 163 if (qoffset < 0) { 164 qoffset = 0; 165 } 166 qskillhight = lastFlyDis - qoffset; 167 } 168 169 /*獲取矩形範圍*/ 170 PolygonCheck rectangle = MoveUtil.getRectangle(centerdir, center.getX(), center.getZ(), qoffset, qskillwidth, qskillhight); 171 /* 172 在獲取場景物件的時候判斷距離由於多邊形和圓形存在切面夾角問題 173 所有在獲取場景物件的時候放寬2碼 174 double radius = lastFlyDis + 2; 175 176 範圍攻擊 177 getFighters(this.center, this.centerVr, type, skillModel.getQrange_limit2(), this.upFlyDis, this.lastFlyDis, this.attackIds, 178 this.attacker, this.target, fighters, qtarget_max, skillModel.getQangle(), skillModel.getQdir()); 179 得到所有可攻擊物件, 180 */ 181 boolean contains = rectangle.contains(px, pz); 182 if (!contains) { 183 /* 184 改物件不在攻擊扇形範圍內 185 return; 186 */ 187 } else { 188 /* 189 記得新增本次可攻擊物件到集合 190 this.attackIds.add(0L); 191 */ 192 } 193 } 194 } 195 196 if (this.attackIds != null) { 197 /*攻擊物件以滿足,不在下一次迴圈執行*/ 198 if (this.attackIds.size() >= attackMAX) { 199 /*需要取消該計算公式*/ 200 this.setCancel(true); 201 } 202 } 203 204 /*設定本次移動時間*/ 205 lastFlyTime = System.currentTimeMillis(); 206 /*設定下一次執行時間*/ 207 if ((flyCount == -1)/*如果是永久執行*/ 208 || (flyCount > 1 && ((getExecCount() / flyArray.length) < flyCount))/*迴圈次數*/) { 209 this.setIntervalTime(flyArray[getExecCount() % flyArray.length]); 210 } else { 211 this.setCancel(true); 212 } 213 } 214 215 }
測試程式碼
public static void main(String[] args) { /*假設技能釋放中心點*/ Vector3 vector3 = new Vector3(3.56, 5.36); /*假設技能釋放朝向*/ Vector v12Vector = MoveUtil.getV12Vector(vector3.getX(), vector3.getZ(), 6.3, 7.2); /*技能釋放動作持續時間是500ms,技能彈道飛行間隔執行時間是100ms*/ int[] intervalTime = new int[]{500, 100}; /*預設技能彈道飛行是死迴圈執行,forcount 用 -1*/ HitTimer hitTimer = new HitTimer(vector3, v12Vector, -1, -1, true, 20, intervalTime); /*內部檢查定時器停止工作*/ }
本次測試程式碼就沒有提供測試結果了;
主要是提供思路,提供遊戲伺服器,在處理傷害計算中,模擬彈道飛行,傷害處理同步機制;
不知道,有沒有哪位大神指點指點其他更好的方式:
PS:我們得到所有可攻擊物件的時候,由於機制問題,你得到的物件順序處理的時候,排序是你查詢到的第一個物件加入,但是第一個物件也行並非是距離中心點最近的物件
1 if (fighters.size() > maxNum) { //超過技能目標人數上限 2 int size = fighters.size(); 3 Person[] mds = fighters.toArray(new Person[0]); 4 if (attack != null) { 5 //先氣泡排序 ->按照從遠到近 6 for (int i = 1; i < mds.length; i++) { 7 for (int j = 0; j < mds.length - i; j++) { 8 Person d1 = mds[j]; 9 Person d2 = mds[j + 1]; 10 if (attack.distance(d1) < attack.distance(d2)) { 11 mds[j] = d2; 12 mds[j + 1] = d1; 13 } 14 } 15 } 16 } 17 int index = 0; 18 while (size > maxNum) { 19 //刪除距離最遠的目標 20 fighters.remove(mds[index]); 21 size--; 22 index++; 23 } 24 }
所有排序所有物件,刪除,最遠的物件開始;得到本次可攻擊物件目標;
謝謝各位大爺們;
看到這裡;
大爺們是不是留個言,或者點個推薦呢???
看在小弟辛辛苦苦,分享的精神上好嘛;
PS: