各位先進好:
我正在練習做一個坦克打磚塊(?)的遊戲,如下圖:
問題是,我有多個If判斷,但它們有相同結果,想請問怎麼樣寫可以減少程式碼。
問題範例(condition A,B 是2不同Boolean函數 甲,乙,丙 是帶入的參數):
//外層if
if ( condition A 甲 )
{
//內層if
if ( condition B 甲 )
{
do the right thing~~~;
}
}
if ( condition A 乙 )
{
if ( condition B 乙 )
{
do the right thing~~~;
}
}
if ( condition A 丙 )
{
if ( condition B 丙 )
{
do the right thing~~~;
}
}
如同上方範例,不同的判斷式做的事都是do the right thing~~~。
因為習慣重複的程式應該可以做刪減,所以想問問有沒有什麼好方法。
我腦海中閃過的是switch,因為它跟我想要的「結果」很像。
switch (condition)
{
case A:
case B:
case C:
do the right thing~~~;
break;
}
以這種方式,多個case做相同的事,但我的條件都是Boolean二分,感覺不符合。
認真想到的可能性是if & logical or,因為它確實可以達到我要的條件篩選和結果。
//外層if
if (condition A 甲 || condition A 乙 || condition A 丙)
{
//內層if
if (condition B 甲 || condition B 乙 || condition B 丙)
{
do the right thing~~~;
}
}
可是,這樣理論上運算效率會下降,
因為原程式的外層if為True時,就會以同樣條件去判斷內層if。
例如 condition A 丙 為True,
就會判斷 condition B 丙 是否為True。
可是if & logical or的運算方式會變成:
判斷condition A 丙 為True以後,
會再從condition B 甲、condition B 乙...一路判斷到condition B 丙。
多做了幾次沒有意義的運算。
其實好像也不是一定要修正,但大腦一直在對我發出「這樣不夠好」的訊號哈哈。
自己越寫越覺得自己不適合寫程式,越想也越糾結,只好上來求助。
貼上真實原始碼作為額外參考(應該非必要?)。
//外層if
if ((hitNum = iHitBlock(Vector2f(tank.left, tank.top)))!=-1)
{
//內層if
if (bInsideBlock(Vector2f(tank.left, tank.top),
sparrBrick[hitNum].getGlobalBounds()))
{
spTank.setPosition(origin);
return;
}
}
if ((hitNum = iHitBlock(Vector2f(tank.left+tank.width,tank.top)))!=-1){
if (bInsideBlock(Vector2f(tank.left+tank.width, tank.top),
sparrBrick[hitNum].getGlobalBounds()))
{
spTank.setPosition(origin);
return;
}
}
if ((hitNum = iHitBlock(Vector2f(tank.left+tank.width,
tank.top+tank.height)))!=-1)
{
if (bInsideBlock(Vector2f(tank.left+tank.width,
tank.top+tank.height),
sparrBrick[hitNum].getGlobalBounds()))
{
spTank.setPosition(origin);
return;
}
}
原始碼中:
外層if
是否 一個點 有包含在 一個磚塊 內部?
共有 8個外層if 代表方形坦克8個點(左上、上、右上、右、右下、下、左下、左)。
內層if
是否 這個點 與 這個磚塊 的相交程度超出容錯範圍?
(之所以這樣做是因為 坦克 和 磚塊 一樣大,如果坦克想從2個磚塊中間通過,很容易卡住,所以設計它就算超出一點點,壓到方塊也沒關係。)
最後如果超出容錯範圍就讓這次Move無效,返回原點。
雖然我上次寫C++應該是五年前的事
但概念上我覺得是差不多的
我認爲你這些判斷式應該是某些動作後的callback會執行的
先從可讀性下手的話
在這個 scope 宣告你需要做判斷的這些數值
Vector2f leftTop = Vector2f(tank.left, tank.top)
Vector2f top = Vector2f(tank.left+tank.width,tank.top)
// ...其他
就會變成
if(iHitBlock(leftTop)!=-1){
// ...
}
if(iHitBlock(top)!=-1){
//...
}
//...
感覺所有判別式裡的
if((hitNum = iHitBlock(Vector2f(tank.left, tank.top)))!=-1)
hitNum
感覺可以不用賦值的吧?
雖然第二層if會用到但感覺他本來應該是外面的scope的變數
感覺是直接將function return value 做判斷就好了
if(iHitBlock(leftTop)!=-1)
因為但它經過每次if判斷都會被重新賦值
經由內部更改外部的變數對於維護或可讀性來說
(就是這個function 不是 pure function )
個人認為不太好
那第二層if就可以改成
if (bInsideBlock(leftTop),
sparrBrick[iHitBlock(leftTop)].getGlobalBounds()))
那最後大概會變成
if(iHitBlock(leftTop)!=-1){
if (bInsideBlock(leftTop),
sparrBrick[iHitBlock(leftTop)].getGlobalBounds())){
//...
}
}
//...
判斷式簡化的問題
在不清楚資料結構的寫法上
基本就是雙層的if其實就是&&
就可以把ㄧ層if給消掉
但是這是在第一層過但第二層沒過的而且也沒有事情要做
的前提下可以這樣寫
如果資料上及行為上跟你提供的原始碼一樣簡單的話
可能將這個八個點作為一個List並用迴圈判斷誰為-1時
就執行bInsideBlock
大概會像是
for(int i = 0; i<positionList.length; i++){
Vector2f position = positionList[i];
if( iHitBlock(position) != -1 ){
if( bInsideBlock(position,
sparrBrick[iHitBlock(position)].getGlobalBounds())){
//...
}
}
}
好懷念的遊戲啊...
我猜你想要的是不是這樣,
if ( condition A 甲 && condition B 甲)
{
do the right thing~~~;
}
else if ( condition A 乙 && condition B 乙)
{
do the right thing~~~;
}
else if ( condition A 丙 && condition B 丙)
{
do the right thing~~~;
}
因為都只有一個if,
就一起判斷就好了,
另外如果滿足第一個if,
後面的else if就不會被執行了,
(如果有可能同時滿足就不要用else if)
但是我也不確定這樣有沒有比較快,
除非你本來速度很慢,
要不然其實感覺不出來,
要不然也可以自己寫抓時間差的部分,
不過時間每次執行也可能會有誤差,
有時候取平均值比較準.
參考看看,寫成 function
語法沒測過
概念上大概像底下這樣
checkIfOutOfBound(tank.left, tank.top);
checkIfOutOfBound(tank.left + tank.width, tank.top);
checkIfOutOfBound(tank.left + tank.width, tank.top + tank.height);
function checkIfOutOfBound(int pLeft, int pTop) {
if ((hitNum = iHitBlock(Vector2f(pLeft, pTop)))!=-1) {
//內層if
if (bInsideBlock(Vector2f(pLeft, pTop),
sparrBrick[hitNum].getGlobalBounds())) {
spTank.setPosition(origin);
return;
}
}
}
使用 command pattern 或 function programming,Java 程式碼可參考:
https://www.geeksforgeeks.org/command-pattern/
Python map 可輕鬆完成以上的事情,但C/C++我就不熟了。