ThatManK Mobile Article
走马灯版本贪吃蛇
####
#!/bin/bash
# 基础知识
# 前景色(文字颜色): echo -e "\e[${num}m有颜色的文字"
# 39默认前景色 30黑色 31红色 32绿色 33黄色 34蓝色 35品红 36青色 37浅灰
# 90深灰色 91红灯 92浅绿色 93淡黄色 94浅蓝 95浅洋红色 96浅青色 97白色
# 背景色: echo -e "\e[$(num)m有背景颜色的文字"
# 49默认背景颜色 40黑色 41红色 42绿色 43黄色 44蓝色 45品红 46青色 47浅灰
# 100深灰色 101红灯 102浅绿色 103淡黄色 104浅蓝 105浅洋红色 106浅青色 107白色
# 属性:
# \e[0m 关闭所有属性
# \e[1m 设置高亮度
# \e[4m 下划线
# \e[5m 闪烁
# \e[7m 反显
# \e[8m 消隐
# \e[30m -- \e[37m 设置前景色
# \e[40m -- \e[47m 设置背景色
# \e[nA 光标上移n行
# \e[nB 光标下移n行
# \e[nC 光标右移n行
# \e[nD 光标左移n行
# \e[y;xH 设置光标位置
# \e[2J 清屏
# \e[K 清除从光标到行尾的内容
# \e[s 保存光标位置
# \e[u 恢复光标位置
# \e[?25l 隐藏光标
# \e[?25h 显示光标
# 属性示例:
# 1 粗体高亮 echo -e "\e[1mBold"
# 2 变暗 echo -e "\e[2mDim"
# 4 下划线 echo -e "\e[4mUnderlined"
# 5 闪烁 echo -e "\e[5mBlink"
# 7 反转 (反转前景色和背景色) echo -e "\e[7minverted"
# 8 隐藏(对密码有用) echo -e "\e[8mHidden"
# 结合:
# echo -e "\e[41;32m文字\e[0m" 红色背景绿色字并清除属性
# echo -e "\e[1;3H*\e[0m" 第一行第三列输出一个*
# echo -e "\e[3;3H\e[44;31m*\e[0m" 第三行第三列输出一个蓝色背景红色字
# echo -e "\e[5;31;42m文字\e[0m" 闪烁、红色背景、绿色的文字并清除属性
# echo -e "\e[3;3H\e[5;31;42m*************\e[0m" 第三行第三列输出一排闪烁的星
# 键盘控制 w s a d
# 退出游戏 q
# 地图大小 长宽可以自由改动
declare -i MAP_WIDTH=60
declare -i MAP_HIGHT=20
# 地图起始位置(左上角的开始绘制的位置, 可以自由改动)
declare -i MAP_X=20
declare -i MAP_Y=5
# 定义 蛇头的起始位置x,y 及 蛇初始化长度4
declare -i X=30
declare -i Y=30
declare -i LENGTH=4
# 舌头蛇身的坐标点
declare -a X_ARR=()
declare -a Y_ARR=()
# 食物的坐标
declare -i RANDOM_X=0
declare -i RANDOM_Y=0
# 跑马灯的开始颜色
declare -i COLOR_INDEX=15
# 初始化 蛇 出生移动方向
DIR='d'
score=0
# 游戏结束恢复环境
function end_of_game() {
kill -9 $!
echo -e "\e[0m"
echo -e "\e[?25h"
exit 1
}
# 生成边界
function init_map() {
# 两横
for i in $(seq ${MAP_X} $((${MAP_X} + ${MAP_WIDTH}))); do
echo -e "\e[${MAP_Y};${i}H@\e[0m"
echo -e "\e[$((${MAP_Y} + ${MAP_HIGHT}));${i}H@\e[0m"
done
# 两竖
for j in $(seq ${MAP_Y} $((${MAP_Y} + ${MAP_HIGHT}))); do
echo -e "\e[${j};${MAP_X}H@\e[0m"
echo -e "\e[${j};$((${MAP_X} + ${MAP_WIDTH}))H@\e[0m"
done
}
# 跑马灯效果
function init_map_lamp() {
while true :; do
let COLOR_INDEX=15
for i in $(seq ${MAP_X} $((${MAP_X} + ${MAP_WIDTH}))); do
let COLOR_INDEX++
sleep 0.003
echo -e "\e[38;5;${COLOR_INDEX}m\e[${MAP_Y};${i}H@\e[0m"
done
for j in $(seq ${MAP_Y} $((${MAP_Y} + ${MAP_HIGHT}))); do
let COLOR_INDEX++
sleep 0.003
echo -e "\e[38;5;${COLOR_INDEX}m\e[${j};$((${MAP_X} + ${MAP_WIDTH}))H@\e[0m"
done
for ((k = $((${MAP_X} + ${MAP_WIDTH})); k >= ${MAP_X}; k--)); do
let COLOR_INDEX--
sleep 0.003
echo -e "\e[38;5;${COLOR_INDEX}m\e[$((${MAP_Y} + ${MAP_HIGHT}));${k}H@\e[0m"
done
for ((m = $((${MAP_Y} + ${MAP_HIGHT})); m >= ${MAP_Y}; m--)); do
let COLOR_INDEX--
sleep 0.003
echo -e "\e[38;5;${COLOR_INDEX}m\e[${m};${MAP_X}H@\e[0m"
done
done
}
# 初始化蛇
function draw_snake() {
clear
# 生成蛇身
for ((i = 1; i < ${LENGTH}; i++)); do
echo -e "\e[${Y_ARR[$i]};${X_ARR[$i]}H*\e[0m"
done
# 生成蛇头
echo -e "\e[${Y_ARR[$i]};${X_ARR[$i]}H$\e[0m"
# 随机生成食物
echo -e "\e[5;32m\e[${RANDOM_Y};${RANDOM_X}H#\e[0m"
# 生成地图
init_map
# 输出蛇头、食物的起位置 打印分数
echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 2));${MAP_X}H\e[32mSnake:${X},${Y}\e[0m\e[0m"
echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 3));${MAP_X}H\e[32mFood :${RANDOM_X},${RANDOM_Y}\e[0m\e[0m"
echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 4));${MAP_X}H\e[33mScore:${score}\e[0m\e[0m"
}
# 生成食物
function gen_food() {
let flag=1
while [ $flag -eq 1 ]; do
# 0-58 + 21
let RANDOM_X=$(($RANDOM % $((${MAP_WIDTH} - 1)) + $((${MAP_X} + 1))))
# 0-18 + 6
let RANDOM_Y=$(($RANDOM % $((${MAP_HIGHT} - 1)) + $((${MAP_Y} + 1))))
# 生成与蛇身+蛇头 坐标不相等的 食物坐标
for ((i = 0; i < ${LENGTH}; i++)); do
# 如果相等 退出当前循环,重新 while循环, i又从1开始
if [[ ${X_ARR[$i]} -eq ${RANDOM_X} && ${Y_ARR[$i]} -eq ${RANDOM_Y} ]]; then
break
fi
done
# 只有生成的食物坐标与蛇头+蛇身坐标完全不相等时
if [ $i -eq ${LENGTH} ]; then
flag=0
fi
done
}
function init() {
clear
# 清除所有属性
echo -e "\e[0m"
# 隐藏光标
echo -e "\e[?25l"
# 生成蛇的初始化坐标
for i in $(seq 5); do
# 在地图的中点取五个点初始化蛇的位置
let pos_x=$((${MAP_WIDTH} / 2 - 2 + ${i}))+${MAP_X}
let pos_y=$(((${MAP_Y} + ${MAP_HIGHT}) / 2))+${MAP_Y}
X_ARR+=($pos_x)
Y_ARR+=($pos_y)
done
X=$pos_x
Y=$pos_y
# 生成食物
gen_food
}
function move() {
case $DIR in
"w") Y=$Y-1 ;; # 往上移动
"s") Y=$Y+1 ;; # 往下移动
"a") X=$X-1 ;; # 往左移动
"q") end_of_game ;;
*) X=$X+1 ;; # 默认右移
esac
# 如果蛇头碰到食物, 长度+1, 并重新生成食物
if [[ $X -eq ${RANDOM_X} && $Y -eq ${RANDOM_Y} ]]; then
((LENGTH++))
let score+=10
X_ARR[${LENGTH}]=$X
Y_ARR[${LENGTH}]=$Y
gen_food
return
fi
# 越界退出
if [[ $X -le $MAP_X || $Y -le $MAP_Y || $X -ge $(($MAP_X + $MAP_WIDTH)) || $Y -ge $(($MAP_Y + $MAP_HIGHT)) ]]; then
end_of_game
fi
# 蛇头碰到蛇身
for ((i = 2; i <= ${LENGTH}; i++)); do
if [[ $X -eq ${X_ARR[$i]} && $Y -eq ${Y_ARR[$i]} ]]; then
end_of_game
fi
done
# 向前蠕动一下
for ((i = 0; i <= ${LENGTH}; i++)); do
X_ARR[$i]=${X_ARR[$i + 1]}
Y_ARR[$i]=${Y_ARR[$i + 1]}
done
# 更新蛇头到数组
X_ARR[${LENGTH}]=$X
Y_ARR[${LENGTH}]=$Y
}
function main() {
init
draw_snake
# 以子进程的方式开启跑马灯效果
{
init_map_lamp
} &
while :; do
OLD_DIR=$DIR
# 如果没有wasd输入, 则保持原方向不变, 否则调转方向
if ! read -n 1 -t 0.5 -s DIR; then
DIR=$OLD_DIR
fi
sleep 0.0001
move
draw_snake
done
}
main