这里就把 Shell 飞机游戏(2013-03-15) 拿来改一个庆祝节日的贪吃蛇吧 :D
贴上游戏截图,一只庆祝快乐的蛇来收集爱心(v 字母像一个爱心吧,就拿它来充当了,没试出打印 ascii 爱心的办法)。
点击(此处)折叠或打开
-
#!/bin/bash
-
# Snake.sh
-
# Created by seesea <seesea2517#126.com> @ 2017-03-08
-
#
-
# 楠妹子发了一个活动,要用代码来庆祝3.8节:http://bbs.chinaunix.net/thread-4260145-1-1.html
-
# 这里就把 Shell 飞机游戏(2013-03-15) 拿来改一个庆祝节日的贪吃蛇吧 :D
-
# 一只庆祝快乐的蛇来收集爱心(v 字母像一个爱心吧,就拿它来充当了,没试出打印 ascii 爱心的办法)
-
# 做为简单 Demo,而且快乐的蛇应该是不死的,所以不做 GameOver 的处理啦
-
-
source colors.sh
-
-
# ============================================================================
-
# 常量
-
# ============================================================================
-
-
# 响应的信号
-
declare -r SIG_UP=SIGRTMIN+1
-
declare -r SIG_DOWN=SIGRTMIN+2
-
declare -r SIG_LEFT=SIGRTMIN+3
-
declare -r SIG_RIGHT=SIGRTMIN+4
-
declare -r SIG_PAUSE=SIGRTMIN+6
-
declare -r SIG_EXIT=SIGRTMIN+7
-
-
# 响应的按键(注意:使用大写配置)
-
declare -r KEY_UP="W"
-
declare -r KEY_DOWN="S"
-
declare -r KEY_LEFT="A"
-
declare -r KEY_RIGHT="D"
-
declare -r KEY_PAUSE="P"
-
declare -r KEY_EXIT="Q"
-
-
# 游戏区域位置大小
-
declare -r GAME_AREA_TOP=30
-
declare -r GAME_AREA_LEFT=40
-
declare -r GAME_AREA_WIDTH=40
-
declare -r GAME_AREA_HEIGHT=20
-
-
# 游戏边界显示字符(分横向和纵向两种字符)
-
declare -r BORDER_H="${BHIG} ${NOR}"
-
declare -r BORDER_V="${BHIG} ${NOR}"
-
-
# ============================================================================
-
# 全局变量/常量
-
# ============================================================================
-
-
declare stty_save # 终端设置
-
declare game_overed=0 # 游戏中止标志
-
declare game_paused=0 # 游戏暂停
-
-
declare pid_loop # 消息循环的进程pid
-
-
declare screen_width # 屏宽
-
declare screen_height # 屏高
-
declare game_map_width # 屏宽
-
declare game_map_height # 屏高
-
-
declare pos_head_r # 当前蛇头坐标:行
-
declare pos_head_c # 当前蛇头坐标:列
-
-
declare move_dir # 移动方向
-
-
declare snake_len # 蛇长
-
declare -a snake # 蛇体数据数组
-
declare -a pos_snake_r # 每一节蛇体的坐标行值
-
declare -a pos_snake_c # 每一节蛇体的坐标列值
-
declare old_tail_pos_r # 移动后的蛇尾位置r
-
declare old_tail_pos_c # 移动后的蛇尾位置c
-
-
declare -ar food_style=( "${RED}v${NOR}" "${GRN}v${NOR}" "${YEL}v${NOR}" "${BLU}v${NOR}" "${MAG}v${NOR}" "${CYN}v${NOR}" "${WHT}v${NOR}" "${HIR}v${NOR}" "${HIG}v${NOR}" "${HIY}v${NOR}" "${HIB}v${NOR}" "${HIM}v${NOR}" "${HIC}v${NOR}" "${HIW}v${NOR}" )
-
declare -r food_style_count=${#food_style[@]}
-
declare food
-
declare food_pos_r
-
declare food_pos_c
-
declare food_style_index
-
-
# ============================================================================
-
# 函数定义
-
# ============================================================================
-
-
# 随机函数
-
# 参数一:随机数的上限+1,缺省为 10
-
function random()
-
{
-
echo $(( RANDOM % ${1:-10} ))
-
}
-
-
# 键盘输入响应函数
-
function Input()
-
{
-
while true
-
do
-
read -s -n 1 -a key
-
key="${key[@]: -1}"
-
case $key in
-
$KEY_UP) sign=$SIG_UP ;;
-
$KEY_DOWN) sign=$SIG_DOWN ;;
-
$KEY_LEFT) sign=$SIG_LEFT ;;
-
$KEY_RIGHT) sign=$SIG_RIGHT ;;
-
$KEY_PAUSE) sign=$SIG_PAUSE ;;
-
$KEY_EXIT) sign=$SIG_EXIT ;;
-
*) continue ;;
-
esac
-
-
kill -s $sign $pid_loop
-
-
# 若是退出按键,则根据游戏循环是否存在来判断是否确认退出
-
if (( sign == SIG_EXIT ))
-
then
-
sleep 0.1
-
if ! ps -p $pid_loop > /dev/null
-
then
-
break
-
fi
-
fi
-
done
-
}
-
-
# 输入动作响应函数
-
# 输入参数一:键盘消息
-
function Action()
-
{
-
sign=$1
-
-
# 若游戏暂停,则只响应暂停信号和退出信号
-
if (( game_paused && sign != SIG_PAUSE && sign != SIG_EXIT ))
-
then
-
return
-
fi
-
-
# 输入线程的暂停处理
-
if (( game_overed && sign == SIG_PAUSE ))
-
then
-
return
-
fi
-
-
case $sign in
-
$SIG_UP) OnPressPlayerMove "U" ;;
-
$SIG_DOWN) OnPressPlayerMove "D" ;;
-
$SIG_LEFT) OnPressPlayerMove "L" ;;
-
$SIG_RIGHT) OnPressPlayerMove "R" ;;
-
$SIG_PAUSE) OnPressGamePause ;;
-
$SIG_EXIT) OnPressGameExit ;;
-
esac
-
}
-
-
# 系统初始化
-
function Init()
-
{
-
screen_width=$(tput cols)
-
screen_height=$(tput lines)
-
-
game_map_width=$GAME_AREA_WIDTH
-
game_map_height=$GAME_AREA_HEIGHT
-
-
# 终端设置
-
stty_save=$(stty -g) # 保存stty配置
-
stty -echo # 关闭输入回显
-
tput civis # 关闭光标
-
shopt -s nocasematch # 开启大小写case比较的开关
-
clear
-
-
# 有时候echo会提示中断的函数调用,目前没啥解决办法,就把错误提示屏蔽了先
-
exec 2> /dev/null
-
}
-
-
# 按键响应:游戏暂停
-
function OnPressGamePause()
-
{
-
game_paused=$(( ! game_paused ))
-
}
-
-
# 按键响应:退出游戏
-
function OnPressGameExit()
-
{
-
game_overed=1
-
}
-
-
# 按键响应:玩家动作
-
# $1: U D L R = up down left right
-
function OnPressPlayerMove()
-
{
-
move_dir="$1"
-
}
-
-
function GameInit()
-
{
-
# 设定输入响应函数
-
# trap Action $SIG_UP $SIG_DOWN $SIG_LEFT $SIG_RIGHT $SIG_PAUSE $SIG_SHOOT $SIG_EXIT
-
trap "Action $SIG_UP" $SIG_UP
-
trap "Action $SIG_DOWN" $SIG_DOWN
-
trap "Action $SIG_LEFT" $SIG_LEFT
-
trap "Action $SIG_RIGHT" $SIG_RIGHT
-
trap "Action $SIG_PAUSE" $SIG_PAUSE
-
trap "Action $SIG_EXIT" $SIG_EXIT
-
-
DrawBorder
-
-
(( pos_head_c = game_map_width / 2 + $GAME_AREA_LEFT - 1 ))
-
(( pos_head_r = game_map_height / 2 + $GAME_AREA_TOP - 1 ))
-
game_overed=0
-
move_dir='X'
-
-
# 测试和截图用 :D snake=(H A P P Y 3 . 8 "${HIM}v${NOR}" "${WHT}v${NOR}" "${RED}v${NOR}" "${GRN}v${NOR}" "${YEL}v${NOR}" "${YEL}v${NOR}" "${BLU}v${NOR}" "${MAG}v${NOR}" "${MAG}v${NOR}" "${CYN}v${NOR}" "${WHT}v${NOR}" "${HIR}v${NOR}" "${HIG}v${NOR}" "${HIY}v${NOR}" "${HIB}v${NOR}" "${MAG}v${NOR}" "${HIM}v${NOR}")
-
snake=(H A P P Y 3 . 8)
-
snake_len=${#snake[*]}
-
for (( i = 0; i < snake_len; ++i ))
-
do
-
pos_snake_r[$i]=$pos_head_r
-
pos_snake_c[$i]=$(( ((pos_head_c + i + 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT ))
-
done
-
-
RandomFood
-
}
-
-
# 游戏循环
-
function GameLoop()
-
{
-
GameInit
-
-
while true
-
do
-
if (( game_overed ))
-
then
-
break
-
fi
-
-
FrameAction
-
sleep 0.04 # 每秒25帧,sleep 0.04
-
done
-
}
-
-
function GameStart()
-
{
-
GameLoop &
-
pid_loop=$!
-
}
-
-
function FrameAction()
-
{
-
# 若游戏暂停,则不进行帧动作
-
if (( game_paused ))
-
then
-
return
-
fi
-
-
MoveSnake
-
if CheckAndEatFood
-
then
-
RandomFood
-
fi
-
-
DrawSnake
-
}
-
-
# 如果有吃到食物,返回真 0,否则返回假 1
-
function CheckAndEatFood()
-
{
-
if (( pos_head_r != food_pos_r || pos_head_c != food_pos_c ))
-
then
-
return 1
-
fi
-
-
snake[$snake_len]=$food
-
pos_snake_r[$snake_len]=$old_tail_pos_r
-
pos_snake_c[$snake_len]=$old_tail_pos_c
-
(( ++snake_len ))
-
-
return 0
-
}
-
-
function MoveSnake()
-
{
-
case $move_dir in
-
'U') (( pos_head_r = ((pos_head_r - 1 + game_map_height - $GAME_AREA_TOP) % game_map_height) + $GAME_AREA_TOP )) ;;
-
'D') (( pos_head_r = ((pos_head_r + 1 + game_map_height - $GAME_AREA_TOP) % game_map_height) + $GAME_AREA_TOP )) ;;
-
'L') (( pos_head_c = ((pos_head_c - 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT )) ;;
-
'R') (( pos_head_c = ((pos_head_c + 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT )) ;;
-
*) return ;;
-
esac
-
-
(( tail_index = snake_len - 1 ))
-
old_tail_pos_r=${pos_snake_r[$tail_index]}
-
old_tail_pos_c=${pos_snake_c[$tail_index]}
-
-
for (( i = snake_len - 1; i >= 1; --i ))
-
do
-
pos_snake_r[$i]=${pos_snake_r[$(( i - 1 ))]}
-
pos_snake_c[$i]=${pos_snake_c[$(( i - 1 ))]}
-
done
-
-
pos_snake_r[0]=$pos_head_r
-
pos_snake_c[0]=$pos_head_c
-
}
-
-
function DrawSnake()
-
{
-
echo -ne "${ESC}[${old_tail_pos_r};${old_tail_pos_c}H "
-
-
for (( i = 0; i < snake_len; ++i ))
-
do
-
pos_r=${pos_snake_r[$i]}
-
pos_c=${pos_snake_c[$i]}
-
-
echo -ne "${ESC}[${pos_r};${pos_c}H${snake[$i]}"
-
done
-
}
-
-
function DrawSnakeXXX()
-
{
-
echo -ne "${ESC}[${old_tail_pos_r};${old_tail_pos_c}H "
-
-
for (( i = 1; i < snake_len - 1; ++i ))
-
do
-
(( j = i - 1 ))
-
pos_r=${pos_snake_r[$j]}
-
pos_c=${pos_snake_c[$j]}
-
-
echo -ne "${ESC}[${pos_r};${pos_c}H${snake[$i]}"
-
done
-
-
echo -ne "${ESC}[${pos_head_r};${pos_head_c}H${snake[0]}"
-
}
-
-
function RandomFood()
-
{
-
# 暂不考虑蛇满屏的时候无法生成新食物的情况
-
while true
-
do
-
(( food_pos_r = $(random game_map_height) + $GAME_AREA_TOP ))
-
(( food_pos_c = $(random game_map_width) + $GAME_AREA_LEFT ))
-
-
again=0
-
for (( i = 0; i < snake_len; ++i ))
-
do
-
if (( pos_snake_r[i] == food_pos_r && pos_snake_c[i] == food_pos_c ))
-
then
-
again=1
-
break
-
fi
-
done
-
-
if (( again == 0 ))
-
then
-
break
-
fi
-
done
-
-
food_style_index=$(random $food_style_count)
-
food=${food_style[$food_style_index]}
-
echo -ne "${ESC}[${food_pos_r};${food_pos_c}H$food"
-
}
-
-
# 退出游戏清理操作
-
function ExitClear()
-
{
-
# 恢复大小写case比较的开关
-
shopt -u nocasematch
-
-
# 恢复stty配置
-
stty $stty_save
-
tput cnorm
-
-
clear
-
}
-
-
# 绘制边界
-
function DrawBorder()
-
{
-
local i
-
local border_h
-
local border_v
-
local r
-
local c
-
local c2
-
-
border_h=""
-
for (( i = 0; i < GAME_AREA_WIDTH + 1; ++i ))
-
do
-
border_h="$border_h$BORDER_H"
-
done
-
-
# 画顶边
-
r=$(( GAME_AREA_TOP - 1 ))
-
c=$(( GAME_AREA_LEFT - 1 ))
-
echo -ne "${ESC}[${r};${c}H${border_h}"
-
-
# 画底边
-
r=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT ))
-
echo -ne "${ESC}[${r};${c}H${border_h}"
-
-
c2=$(( GAME_AREA_LEFT - 1 + GAME_AREA_WIDTH + 1 ))
-
for (( r = GAME_AREA_TOP - 1; r < GAME_AREA_TOP + GAME_AREA_HEIGHT + 1; ++r ))
-
do
-
echo -ne "${ESC}[${r};${c}H${BORDER_V}${ESC}[${r};${c2}H${BORDER_V}"
-
done
-
}
-
-
# 主函数
-
function Main()
-
{
-
Init
-
-
GameStart
-
Input
-
ExitClear
-
}
-
- Main