最近看了一个shell写的游戏:俄罗斯方块(下方有下载),受其启发重写了之前的一个小游戏:贪吃蛇。
之前写的那个需要用到临时文件, 每个动作都需要多次读写,效率不行,看了别人的思路之后,很快就
重写一个。
以下变量定义了三部分内容:信号、边框属性和游戏本身PID,并对游戏等级做定义注释。
#signal upper=20 down=21 left=22 right=23 stop=24 stopI=25 pause=26 start=27 speedup=28 slowdown=29 quit=30 height=4 width=10 HEIGHT=20 WIDTH=30 upper_boundary=$((height+1)) down_boundary=$((height+HEIGHT+2)) left_boundary=$((width+1)) right_boundary=$((width+WIDTH+2)) MYPID=$$ #About the level and the score. #time(s) 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.0 #level 1 2 3 4 5 6 7 8 9 10
display_random_point函数用来在特定区域得到并显示一随机块。
#get one random point in the border.
display_random_point(){
local i succ=0
while :; do
while :; do
point1=$((RANDOM%down_boundary))
[[ $point1 -gt $upper_boundary ]] && [[ $point1 -lt $down_boundary ]] && break
done
while :; do
point2=$((RANDOM%right_boundary))
[[ $point2 -gt $left_boundary ]] && [[ $point1 -lt $right_boundary ]] && break
done
#for sure, not in the body points.
for((i=0; i<${#snake_body[@]}; i++)); do
[[ "$point1 $point2" == "${snake_body[i]} ${snake_body[i+1]}" ]] && { succ=1; break; }
done
[[ $succ -eq 0 ]] && break
done
random_point=($point1 $point2)
echo -ne "\033[${point1};${point2}H\033[44m \033[0m"
}
exit_game 退出游戏
exit_game(){
kill -$stopI $MYPID
tput cnorm
echo -e "\033[$(((height+HEIGHT+2)/2));$(((width+WIDTH)/2))H\033[33mSnake Dead !!\033[0m"
echo -e "\033[$((height+HEIGHT+3));$((width+WIDTH+3))H \033[0m"
exit
}
display_element 后台运行函数, 用来捕捉信号、显示游戏边框等信息以及移动蛇身
display_element(){
#隐藏鼠标
tput civis #Hide cursor
#捕捉信号
trap 'DIRECTION=upper' $upper
trap 'DIRECTION=down' $down
trap 'DIRECTION=left' $left
trap 'DIRECTION=right' $right
trap 'STOP=1' $stop
trap 'IFPAUSE=1' $pause
trap 'IFPAUSE=0' $start
trap '((level++))' $speedup
trap '((level--))' $slowdown
trap 'exit_game' $quit
#定义部分局部变量
score=0
level=5
STOP=0
DIRECTION=right
old_direction=$DIRECTION
snake_body=( $((height+2)) $((width+2)) )
#画边框
local i
for((i=height+1; i<=height+HEIGHT+2; i++))
do
echo -ne "\033[${i};$((width+1))H\033[42m \033[0m"
echo -ne "\033[${i};$((width+WIDTH+2))H\033[42m \033[0m"
done
for((i=width+1; i<=width+WIDTH+2; i++))
do
echo -ne "\033[$((height+1));${i}H\033[42m \033[0m"
echo -ne "\033[$((height+HEIGHT+2));${i}H\033[42m \033[0m"
done
#显示成绩、等级和运行状态
echo -ne "\033[${snake_body[0]};${snake_body[1]}H\033[41m \033[0m"
echo -ne "\033[$((height+2));$((width+WIDTH+3))H\033[1;34m Score: \033[1;39m$score\033[0m"
echo -ne "\033[$((height+4));$((width+WIDTH+3))H\033[1;34m Level: \033[1;35m$level\033[0m"
echo -ne "\033[$((height+6));$((width+WIDTH+3))H\033[1;34m State: \033[1;5;32mrunning\033[0m"
echo -ne "\033[${height};$((width+7))H\033[35m[ Snake ] by LingYi\033[0m"
display_random_point
#后台循环, 移动蛇身
while [[ $STOP -ne 1 ]]
do
#不允许相反方向移动
case $old_direction in
'upper') [[ $DIRECTION == 'down' ]] && DIRECTION=upper ;;
'down' ) [[ $DIRECTION == 'upper' ]] && DIRECTION=down ;;
'left' ) [[ $DIRECTION == 'right' ]] && DIRECTION=left ;;
'right') [[ $DIRECTION == 'left' ]] && DIRECTION=right ;;
esac
#得到新蛇头
new_head[0]=${snake_body[$((${#snake_body[@]}-2))]}
new_head[1]=${snake_body[$((${#snake_body[@]}-1))]}
case $DIRECTION in
'upper') ((new_head[0]-=1)) ;;
'down' ) ((new_head[0]+=1)) ;;
'left' ) ((new_head[1]-=1)) ;;
'right') ((new_head[1]+=1)) ;;
esac
#判断边框碰撞
case $DIRECTION in
'upper') [[ ${new_head[0]} -eq $upper_boundary ]] && exit_game ;;
'down' ) [[ ${new_head[0]} -eq $down_boundary ]] && exit_game ;;
'left' ) [[ ${new_head[1]} -eq $left_boundary ]] && exit_game ;;
'right') [[ ${new_head[1]} -eq $right_boundary ]] && exit_game ;;
esac
#判断蛇身碰撞
local i
for((i=0; i<${#snake_body[@]}; i++)); do
[[ "${new_head[@]}" == "${snake_body[i]} ${snake_body[i+1]}" ]] && exit_game
done
#暂停、继续
while [[ $IFPAUSE -eq 1 ]]; do
echo -ne "\033[$((height+6));$((width+WIDTH+3))H\033[1;34m State: \033[1;5;31mpause \033[0m"
done
echo -ne "\033[$((height+6));$((width+WIDTH+3))H\033[1;34m State: \033[1;32mrunning\033[0m"
#加速、减速
if [[ $old_level != $level ]]; then
echo -ne "\033[$((height+4));$((width+WIDTH+3))H\033[1;34m Level: \033[1;35m$level\033[0m"
fi
old_level=$level
#吃到随机块,显示成绩和级别信息
if [[ ${new_head[@]} == ${random_point[@]} ]]; then
snake_body=( ${snake_body[@]} ${new_head[@]} )
display_random_point
let score++
[[ $((score%20)) -eq 0 ]] && let level++
echo -ne "\033[$((height+2));$((width+WIDTH+3))H\033[1;34m Score: \033[1;39m$score\033[0m"
echo -ne "\033[$((height+4));$((width+WIDTH+3))H\033[1;34m Level: \033[1;35m$level\033[0m"
else
echo -ne "\033[${snake_body[0]};${snake_body[1]}H \033[0m"
snake_body=( $(echo ${snake_body[@]} | cut -d ' ' -f 3-) ${new_head[@]})
fi
echo -ne "\033[${new_head[0]};${new_head[1]}H\033[41m \033[0m"
old_direction=$DIRECTION
sleep 0.$((10-level))
done
}
以下是运行主体代码, 主要是运行后台函数、捕捉按键和发送信号。
clear
display_element &
back_pid=$!
trap 'EXIT=1' $stopI
trap 'kill -$stop $back_pid; stty echo; exit_game' 2
ESC=`echo -e '\033'`
while :; do
read -s -n 1 key
key=`echo $key | tr 'a-z' 'A-Z'`
[[ $key == '' ]] && sig=$pause # 空格:暂停
[[ $key == 'C' ]] && sig=$start # C键:继续
[[ $key == 'U' ]] && sig=$speedup # U键:加速
[[ $key == 'O' ]] && sig=$slowdown # O键:减速
[[ $key == 'Q' ]] && sig=$quit #Q键:退出
#改变移动方向
[[ $key == 'W' ]] || [[ $key == 'I' ]] && sig=$upper
[[ $key == 'S' ]] || [[ $key == 'K' ]] && sig=$down
[[ $key == 'D' ]] || [[ $key == 'L' ]] && sig=$right
[[ $key == 'A' ]] || [[ $key == 'J' ]] && sig=$left
[[ $key == $ESC ]] && {
for (( i=0; i<=1; i++ )); do read -s -n 1 KEY[$i]; done
[[ ${KEY[0]} == $ESC ]] && sig=$stop
[[ ${KEY[0]} == '[' ]] && {
[[ ${KEY[1]} == 'A' ]] && sig=$upper
[[ ${KEY[1]} == 'B' ]] && sig=$down
[[ ${KEY[1]} == 'C' ]] && sig=$right
[[ ${KEY[1]} == 'D' ]] && sig=$left
}
}
#发送信号
[[ $EXIT -ne 1 ]] && kill -$sig $back_pid || break
done
演示:
下载:
转载于:https://blog.51cto.com/lingyi/1743976