一個能打遊戲的shell指令碼(太牛了)
阿新 • • 發佈:2018-12-28
#!/bin/bash APP_NAME="${0##*[\\/]}" APP_VERSION="1.0" #顏色定義 iSumColor=7 #顏色總數 cRed=1 #紅色 cGreen=2 #綠色 cYellow=3 #××× cBlue=4 #藍色 cFuchsia=5 #紫紅色 cCyan=6 #青色(藍綠色) cWhite=7 #白色 #位置與大小 marginLeft=3 #邊框左邊距 marginTop=2 #邊框上邊距 ((mapLeft=marginLeft+2)) #棋盤左邊距 ((mapTop=$marginTop+1)) #棋盤上邊距 mapWidth=10 #棋盤寬度 mapHeight=15 #棋盤高度 #顏色設定 cBorder=$cGreen cScore=$cFuchsia cScoreValue=$cCyan #控制訊號 #遊戲使用兩個程序,一個用於接收輸入,一個用於遊戲流程和顯示介面; #當前者接收到上下左右等按鍵時,通過向後者傳送signal的方式通知後者。 sigRotate=25 #向上鍵 sigLeft=26 sigRight=27 sigDown=28 sigAllDown=29 #空格鍵 sigExit=30 #方塊定義,7大類19種樣式 #前8位為方塊座標,後2位為方塊剛出現的時候的位置 box0_0=(0 0 0 1 1 0 1 1 0 4) box1_0=(0 1 1 1 2 1 3 1 0 3) box1_1=(1 0 1 1 1 2 1 3 -1 3) box2_0=(0 0 1 0 1 1 2 1 0 4) box2_1=(0 1 0 2 1 0 1 1 0 3) box3_0=(0 1 1 0 1 1 2 0 0 4) box3_1=(0 0 0 1 1 1 1 2 0 4) box4_0=(0 2 1 0 1 1 1 2 0 3) box4_1=(0 1 1 1 2 1 2 2 0 3) box4_2=(1 0 1 1 1 2 2 0 -1 3) box4_3=(0 0 0 1 1 1 2 1 0 4) box5_0=(0 0 1 0 1 1 1 2 0 3) box5_1=(0 1 0 2 1 1 2 1 0 3) box5_2=(1 0 1 1 1 2 2 2 -1 3) box5_3=(0 1 1 1 2 0 2 1 0 4) box6_0=(0 1 1 0 1 1 1 2 0 3) box6_1=(0 1 1 1 1 2 2 1 0 3) box6_2=(1 0 1 1 1 2 2 1 -1 3) box6_3=(0 1 1 0 1 1 2 1 0 4) iSumType=7 #方塊型別總數 boxStyle=(1 2 2 2 4 4 4) #各種方塊旋轉後可能的樣式數目 iScoreEachLevel=50 #提升一個級別需要的分數 #執行時資料 sig=0 #接收到的signal iScore=0 #總分 iLevel=0 #速度級 boxNext=() #下一個方塊 iboxNextColor=0 #下一個方塊的顏色 iboxNextType=0 #下一個方塊的種類 iboxNextStyle=0 #下一個方塊的樣式 boxCur=() #當前方塊的位置定義 iBoxCurColor=0 #當前方塊的顏色 iBoxCurType=0 #當前方塊的種類 iBoxCurStyle=0 #當前方塊的樣式 boxCurX=-1 #當前方塊的x座標位置 boxCurY=-1 #當前方塊的y座標位置 map=() #棋盤圖表 #初始化所有背景方塊為-1, 表示沒有方塊 for ((i = 0; i < mapHeight * mapWidth; i++)) do map[$i]=-1 done #接收輸入的程序的主函式 function RunAsKeyReceiver() { local pidDisplayer key aKey sig cESC sTTY pidDisplayer=$1 aKey=(0 0 0) cESC=`echo -ne "\033"` cSpace=`echo -ne "\040"` #儲存終端屬性。在read -s讀取終端鍵時,終端的屬性會被暫時改變。 #如果在read -s時程式被不幸殺掉,可能會導致終端混亂, #需要在程式退出時恢復終端屬性。 sTTY=`stty -g` #捕捉退出訊號 trap "MyExit;" INT QUIT trap "MyExitNoSub;" $sigExit #隱藏游標 echo -ne "\033[?25l" while : do #讀取輸入。注-s不回顯,-n讀到一個字元立即返回 read -s -n 1 key aKey[0]=${aKey[1]} aKey[1]=${aKey[2]} aKey[2]=$key sig=0 #判斷輸入了何種鍵 if [[ $key == $cESC && ${aKey[1]} == $cESC ]] then #ESC鍵 MyExit elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]] then if [[ $key == "A" ]]; then sig=$sigRotate #<向上鍵> elif [[ $key == "B" ]]; then sig=$sigDown #<向下鍵> elif [[ $key == "D" ]]; then sig=$sigLeft #<向左鍵> elif [[ $key == "C" ]]; then sig=$sigRight #<向右鍵> fi elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate #W, w elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown #S, s elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft #A, a elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight #D, d elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown #空格鍵 elif [[ $key == "Q" || $key == "q" ]] #Q, q then MyExit fi if [[ $sig != 0 ]] then #向另一程序傳送訊息 kill -$sig $pidDisplayer fi done } #退出前的恢復 MyExitNoSub() { local y #恢復終端屬性 stty $sTTY ((y = marginTop + mapHeight + 4)) #顯示游標 echo -e "\033[?25h\033[${y};0H" exit } MyExit() { #通知顯示程序需要退出 kill -$sigExit $pidDisplayer MyExitNoSub } #處理顯示和遊戲流程的主函式 RunAsDisplayer() { local sigThis InitDraw #掛載各種訊號的處理函式 trap "sig=$sigRotate;" $sigRotate trap "sig=$sigLeft;" $sigLeft trap "sig=$sigRight;" $sigRight trap "sig=$sigDown;" $sigDown trap "sig=$sigAllDown;" $sigAllDown trap "ShowExit;" $sigExit while : do #根據當前的速度級iLevel不同,設定相應的迴圈的次數 for ((i = 0; i < 21 - iLevel; i++)) do sleep 0.02 sigThis=$sig sig=0 #根據sig變數判斷是否接受到相應的訊號 if ((sigThis == sigRotate)); then BoxRotate; #旋轉 elif ((sigThis == sigLeft)); then BoxLeft; #左移一列 elif ((sigThis == sigRight)); then BoxRight; #右移一列 elif ((sigThis == sigDown)); then BoxDown; #下落一行 elif ((sigThis == sigAllDown)); then BoxAllDown; #下落到底 fi done #kill -$sigDown $$ BoxDown #下落一行 done } #繪製當前方塊,傳第一個引數,0表示擦除當前方塊,1表示繪製當前方塊 DrawCurBox() { local i x y bErase sBox bErase=$1 if (( bErase == 0 )) then sBox="\040\040" #用兩個空格擦除 else sBox="[]" echo -ne "\033[1m\033[3${iBoxCurColor}m\033[4${iBoxCurColor}m" fi for ((i = 0; i < 8; i += 2)) do ((y = mapTop + 1 + ${boxCur[$i]} + boxCurY)) ((x = mapLeft + 1 + 2 * (boxCurX + ${boxCur[$i + 1]}))) echo -ne "\033[${y};${x}H${sBox}" done echo -ne "\033[0m" } #移動方塊 #BoxMove(y, x), 測試是否可以把移動中的方塊移到(y, x)的位置, 返回0則可以, 1不可以 BoxMove() { local i x y xPos yPos yPos=$1 xPos=$2 for ((i = 0; i < 8; i += 2)) do #方塊相對於棋盤座標 ((y = yPos + ${boxCur[$i]})) ((x = xPos + ${boxCur[$i + 1]})) if (( y < 0 || y >= mapHeight || x < 0 || x >= mapWidth)) then #撞到牆壁了 return 1 fi if (( ${map[y * mapWidth + x]} != -1 )) then #撞到其他已經存在的方塊了 return 1 fi done return 0; } #將方塊貼到棋盤上 Box2Map() { local i j x y line #將當前移動中的方塊貼到棋盤對應的區域 for ((i = 0; i < 8; i += 2)) do #計算方塊相對於棋盤的座標 ((y = ${boxCur[$i]} + boxCurY)) ((x = ${boxCur[$i + 1]} + boxCurX)) map[y*mapWidth+x]=$iBoxCurColor #將方塊顏色賦給地圖 done line=0 for ((i = 0; i < mapHeight; i++)) do for ((j = 0; j < mapWidth; j++)) do #如果棋盤上有空隙,跳出迴圈 [[ ${map[i*mapWidth+j]} -eq -1 ]] && break done [ $j -lt $mapWidth ] && continue #說明當前行可消去,可消去行數加一 (( line++ )) #第i行可被消除,將0行至第i-1行全部下移一行,從第i-1行開始移動 for ((j = i*mapWidth-1; j >= 0; j--)) do ((x = j + mapWidth)) map[$x]=${map[$j]} done #因為下移一行,第0行置空 for ((i = 0; i < mapWidth; i++)) do map[$i]=-1 done done [ $line -eq 0 ] && return #根據消去的行數line計算分數和速度級 ((x = marginLeft + mapWidth * 2 + 7)) ((y = marginTop + 11)) ((iScore += line * 2 - 1)) #顯示新的分數 echo -ne "\033[1m\033[3${cScoreValue}m\033[${y};${x}H${iScore} " if ((iScore % iScoreEachLevel < line * 2 - 1)) then if ((iLevel < 20)) then ((iLevel++)) ((y = marginTop + 14)) #顯示新的速度級 echo -ne "\033[3${cScoreValue}m\033[${y};${x}H${iLevel} " fi fi echo -ne "\033[0m" #重新顯示背景方塊 for ((i = 0; i < mapHeight; i++)) do #棋盤相對於螢幕的座標 ((y = i + mapTop + 1)) ((x = mapLeft + 1)) echo -ne "\033[${y};${x}H" for ((j = 0; j < mapWidth; j++)) do ((tmp = i * mapWidth + j)) if ((${map[$tmp]} == -1)) then echo -ne " " else echo -ne "\033[1m\033[3${map[$tmp]}m\033[4${map[$tmp]}m[]\033[0m" fi done done } #左移一格 BoxLeft() { local x ((x = boxCurX - 1)) if BoxMove $boxCurY $x then DrawCurBox 0 ((boxCurX = x)) DrawCurBox 1 fi } #右移一格 BoxRight() { local x ((x = boxCurX + 1)) if BoxMove $boxCurY $x then DrawCurBox 0 ((boxCurX = x)) DrawCurBox 1 fi } #向下移一格 BoxDown() { local y ((y = boxCurY + 1)) #新的y座標 if BoxMove $y $boxCurX #測試是否可以下落一行 then DrawCurBox 0 #將舊的方塊抹去 ((boxCurY = y)) DrawCurBox 1 #顯示新的下落後方塊 else #走到這兒, 如果不能下落了 Box2Map #將當前移動中的方塊貼到背景方塊中 CreateBox #產生新的方塊 fi } #下落到底 BoxAllDown() { local y iDown #計算能夠下落的行數 iDown=0 (( y = boxCurY + 1 )) while BoxMove $y $boxCurX do (( y++ )) (( iDown++ )) done DrawCurBox 0 #將舊的方塊抹去 ((boxCurY += iDown)) DrawCurBox 1 #顯示新的下落後的方塊 Box2Map #將當前移動中的方塊貼到背景方塊中 CreateBox #產生新的方塊 } #翻轉 BoxRotate() { [ ${boxStyle[$iBoxCurType]} -eq 1 ] && return ((rotateStyle = (iBoxCurStyle + 1) % ${boxStyle[$iBoxCurType]})) #將當前方塊儲存到boxTmp boxTmp=( `eval 'echo ${boxCur[@]}'` ) boxCur=( `eval 'echo ${box'$iBoxCurType'_'$rotateStyle'[@]}'` ) if BoxMove $boxCurY $boxCurX #測試旋轉後是否有空間放的下 then #抹去舊的方塊 boxCur=( `eval 'echo ${boxTmp[@]}'` ) DrawCurBox 0 boxCur=( `eval 'echo ${box'$iBoxCurType'_'$rotateStyle'[@]}'` ) DrawCurBox 1 iBoxCurStyle=$rotateStyle else #不能旋轉,還是繼續使用老的樣式 boxCur=( `eval 'echo ${boxTmp[@]}'` ) fi } #準備下一個方塊 PrepareNextBox() { local i x y #清除右邊預顯示的方塊 if (( ${#boxNext[@]} != 0 )); then for ((i = 0; i < 8; i += 2)) do ((y = marginTop + 1 + ${boxNext[$i]})) ((x = marginLeft + 2 * mapWidth + 7 + 2 * ${boxNext[$i + 1]})) echo -ne "\033[${y};${x}H\040\040" done fi #隨機生成預顯式方塊 (( iBoxNextType = RANDOM % iSumType )) (( iBoxNextStyle = RANDOM % ${boxStyle[$iBoxNextType]} )) (( iBoxNextColor = RANDOM % $iSumColor + 1 )) boxNext=( `eval 'echo ${box'$iBoxNextType'_'$iBoxNextStyle'[@]}'` ) #顯示右邊預顯示的方塊 echo -ne "\033[1m\033[3${iBoxNextColor}m\033[4${iBoxNextColor}m" for ((i = 0; i < 8; i += 2)) do ((y = marginTop + 1 + ${boxNext[$i]})) ((x = marginLeft + 2 * mapWidth + 7 + 2 * ${boxNext[$i + 1]})) echo -ne "\033[${y};${x}H[]" done echo -ne "\033[0m" } #顯示新方塊 CreateBox() { if (( ${#boxCur[@]} == 0 )); then #當前方塊不存在 (( iBoxCurType = RANDOM % iSumType )) (( iBoxCurStyle = RANDOM % ${boxStyle[$iBoxCurType]} )) (( iBoxCurColor = RANDOM % $iSumColor + 1 )) else #當前方塊已存在, 將下一個方塊賦給當前方塊 iBoxCurType=$iBoxNextType; iBoxCurStyle=$iBoxNextStyle; iBoxCurColor=$iBoxNextColor fi #當前方塊陣列 boxCur=( `eval 'echo ${box'$iBoxCurType'_'$iBoxCurStyle'[@]}'` ) #初始化方塊起始座標 boxCurY=boxCur[8]; boxCurX=boxCur[9]; DrawCurBox 1 #繪製當前方塊 if ! BoxMove $boxCurY $boxCurX then kill -$sigExit $PPID ShowExit fi PrepareNextBox } #繪製邊框 DrawBorder() { clear local i y x1 x2 #顯示邊框 echo -ne "\033[1m\033[3${cBorder}m\033[4${cBorder}m" ((x1 = marginLeft + 1)) #左邊框x座標 ((x2 = x1 + 2 + mapWidth * 2)) #右邊框x座標 for ((i = 0; i < mapHeight; i++)) do ((y = i + marginTop + 2)) echo -ne "\033[${y};${x1}H||" #繪製左邊框 echo -ne "\033[${y};${x2}H||" #繪製右邊框 done ((x1 = marginTop + mapHeight + 2)) for ((i = 0; i < mapWidth + 2; i++)) do ((y = i * 2 + marginLeft + 1)) echo -ne "\033[${mapTop};${y}H==" #繪製上邊框 echo -ne "\033[${x1};${y}H==" #繪製下邊框 done echo -ne "\033[0m" #顯示"Score"和"Level"字樣 echo -ne "\033[1m" ((y = marginLeft + mapWidth * 2 + 7)) ((x1 = marginTop + 10)) echo -ne "\033[3${cScore}m\033[${x1};${y}HScore" ((x1 = marginTop + 11)) echo -ne "\033[3${cScoreValue}m\033[${x1};${y}H${iScore}" ((x1 = marginTop + 13)) echo -ne "\033[3${cScore}m\033[${x1};${y}HLevel" ((x1 = marginTop + 14)) echo -ne "\033[3${cScoreValue}m\033[${x1};${y}H${iLevel}" echo -ne "\033[0m" } InitDraw() { clear #清屏 DrawBorder #繪製邊框 CreateBox #建立方塊 } #退出時顯示GameOVer! ShowExit() { local y ((y = mapHeight + mapTop + 3)) echo -e "\033[${y};1HGameOver!\033[0m" exit } #遊戲主程式在這兒開始. if [[ "$1" == "--version" ]]; then echo "$APP_NAME $APP_VERSION" elif [[ "$1" == "--show" ]]; then #當發現具有引數--show時,執行顯示函式 RunAsDisplayer else bash $0 --show& #以引數--show將本程式再執行一遍 RunAsKeyReceiver $! #以上一行產生的程序的程序號作為引數 fi keytest.sh #!/bin/bash GetKey() { aKey=(0 0 0) #定義一個數組來儲存3個按鍵 cESC=`echo -ne "\033"` cSpace=`echo -ne "\040"` while : do read -s -n 1 key #讀取一個字元,將讀取到的字元儲存在key中 #echo $key #echo XXX aKey[0]=${aKey[1]} #第一個按鍵 aKey[1]=${aKey[2]} #第二個按鍵 aKey[2]=$key #第三個按鍵 if [[ $key == $cESC && ${aKey[1]} == $cESC ]] then MyExit elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]] then if [[ $key == "A" ]]; then echo KEYUP elif [[ $key == "B" ]]; then echo KEYDOWN elif [[ $key == "D" ]]; then echo KEYLEFT elif [[ $key == "C" ]]; then echo KEYRIGHT fi fi done } GetKey draw.sh #!/bin/bash #位置與大小 marginLeft=8 #邊框左邊距 marginTop=6 #邊框上邊距 ((mapLeft=marginLeft+2)) #棋盤左邊距 ((mapTop=$marginTop+1)) #棋盤上邊距 mapWidth=10 #棋盤寬度 mapHeight=15 #棋盤高度 #方塊定義,7大類19種樣式 #前8位為方塊座標,後2位為方塊剛出現的時候的位置 box0_0=(0 0 0 1 1 0 1 1 0 4) box1_0=(0 1 1 1 2 1 3 1 0 3) box1_1=(1 0 1 1 1 2 1 3 -1 3) box2_0=(0 0 1 0 1 1 2 1 0 4) box2_1=(0 1 0 2 1 0 1 1 0 3) box3_0=(0 1 1 0 1 1 2 0 0 4) box3_1=(0 0 0 1 1 1 1 2 0 4) box4_0=(0 2 1 0 1 1 1 2 0 3) box4_1=(0 1 1 1 2 1 2 2 0 3) box4_2=(1 0 1 1 1 2 2 0 -1 3) box4_3=(0 0 0 1 1 1 2 1 0 4) box5_0=(0 0 1 0 1 1 1 2 0 3) box5_1=(0 1 0 2 1 1 2 1 0 3) box5_2=(1 0 1 1 1 2 2 2 -1 3) box5_3=(0 1 1 1 2 0 2 1 0 4) box6_0=(0 1 1 0 1 1 1 2 0 3) box6_1=(0 1 1 1 1 2 2 1 0 3) box6_2=(1 0 1 1 1 2 2 1 -1 3) box6_3=(0 1 1 0 1 1 2 1 0 4) #繪製邊框 DrawBorder() { clear local i y x1 x2 #顯示邊框 echo -ne "\033[1m\033[32m\033[42m" ((x1 = marginLeft + 1)) #左邊框x座標 ((x2 = x1 + 2 + mapWidth * 2)) #右邊框x座標 for ((i = 0; i < mapHeight; i++)) do ((y = i + marginTop + 2)) echo -ne "\033[${y};${x1}H||" #繪製左邊框 echo -ne "\033[${y};${x2}H||" #繪製右邊框 done ((x1 = marginTop + mapHeight + 2)) for ((i = 0; i < mapWidth + 2; i++)) do ((y = i * 2 + marginLeft + 1)) echo -ne "\033[${mapTop};${y}H==" #繪製上邊框 echo -ne "\033[${x1};${y}H==" #繪製下邊框 done echo -ne "\033[0m" } DrawBox() { local i x y xPos yPos yPos=${box0_0[8]} xPos=${box0_0[9]} echo -ne "\033[1m\033[35m\033[45m" for ((i = 0; i < 8; i += 2)) do (( y = mapTop + 1 + ${box0_0[$i]} + yPos )) (( x = mapLeft + 1 + 2 * (${box0_0[$i + 1]} + xPos) )) echo -ne "\033[${y};${x}H[]" done echo -ne "\033[0m" } InitDraw() { clear #清屏 DrawBorder #繪製邊框 DrawBox while : do sleep 1 done } InitDraw
說明:⬅️➡️是控制方塊方向的,⬆️是轉換方塊的,⬇️是方塊加速下落的,沒有暫停鍵
效果圖:
友情下載:
如果複製本文的指令碼到你的linux伺服器發生了格式錯亂,可以通過博主的百度雲連結下載原版:
連結: https://pan.baidu.com/s/10BhOxjmxo0BXyeWPVwdJDA 提取碼: 6xnx
微信掃碼獲取: