1. 程式人生 > >一個能打遊戲的shell指令碼(太牛了)

一個能打遊戲的shell指令碼(太牛了)

#!/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

說明:⬅️➡️是控制方塊方向的,⬆️是轉換方塊的,⬇️是方塊加速下落的,沒有暫停鍵

效果圖:
一個能打遊戲的shell指令碼(太牛了)
友情下載:
如果複製本文的指令碼到你的linux伺服器發生了格式錯亂,可以通過博主的百度雲連結下載原版:
連結: https://pan.baidu.com/s/10BhOxjmxo0BXyeWPVwdJDA 提取碼: 6xnx

微信掃碼獲取:
一個能打遊戲的shell指令碼(太牛了)