Chinaunix首页 | 论坛 | 博客
  • 博客访问: 90033
  • 博文数量: 6
  • 博客积分: 155
  • 博客等级:
  • 技术积分: 388
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-27 11:13
文章分类

全部博文(6)

文章存档

2021年(1)

2017年(1)

2013年(2)

2012年(2)

我的朋友

分类: LINUX

2017-03-09 16:34:29

楠妹子发了一个活动,要用代码来庆祝3.8节:。
这里就把 Shell 飞机游戏(2013-03-15) 拿来改一个庆祝节日的贪吃蛇吧 :D
贴上游戏截图,一只庆祝快乐的蛇来收集爱心(v 字母像一个爱心吧,就拿它来充当了,没试出打印 ascii 爱心的办法)。

   
    

点击(此处)折叠或打开

  1. #!/bin/bash
  2. # Snake.sh
  3. # Created by seesea <seesea2517#126.com> @ 2017-03-08
  4. #
  5. # 楠妹子发了一个活动,要用代码来庆祝3.8节:http://bbs.chinaunix.net/thread-4260145-1-1.html
  6. # 这里就把 Shell 飞机游戏(2013-03-15) 拿来改一个庆祝节日的贪吃蛇吧 :D
  7. # 一只庆祝快乐的蛇来收集爱心(v 字母像一个爱心吧,就拿它来充当了,没试出打印 ascii 爱心的办法)
  8. # 做为简单 Demo,而且快乐的蛇应该是不死的,所以不做 GameOver 的处理啦

  9. source colors.sh

  10. # ============================================================================
  11. # 常量
  12. # ============================================================================

  13. # 响应的信号
  14. declare -r SIG_UP=SIGRTMIN+1
  15. declare -r SIG_DOWN=SIGRTMIN+2
  16. declare -r SIG_LEFT=SIGRTMIN+3
  17. declare -r SIG_RIGHT=SIGRTMIN+4
  18. declare -r SIG_PAUSE=SIGRTMIN+6
  19. declare -r SIG_EXIT=SIGRTMIN+7

  20. # 响应的按键(注意:使用大写配置)
  21. declare -r KEY_UP="W"
  22. declare -r KEY_DOWN="S"
  23. declare -r KEY_LEFT="A"
  24. declare -r KEY_RIGHT="D"
  25. declare -r KEY_PAUSE="P"
  26. declare -r KEY_EXIT="Q"

  27. # 游戏区域位置大小
  28. declare -r GAME_AREA_TOP=30
  29. declare -r GAME_AREA_LEFT=40
  30. declare -r GAME_AREA_WIDTH=40
  31. declare -r GAME_AREA_HEIGHT=20

  32. # 游戏边界显示字符(分横向和纵向两种字符)
  33. declare -r BORDER_H="${BHIG} ${NOR}"
  34. declare -r BORDER_V="${BHIG} ${NOR}"

  35. # ============================================================================
  36. # 全局变量/常量
  37. # ============================================================================

  38. declare stty_save # 终端设置
  39. declare game_overed=0 # 游戏中止标志
  40. declare game_paused=0 # 游戏暂停

  41. declare pid_loop # 消息循环的进程pid

  42. declare screen_width # 屏宽
  43. declare screen_height # 屏高
  44. declare game_map_width # 屏宽
  45. declare game_map_height # 屏高

  46. declare pos_head_r # 当前蛇头坐标:行
  47. declare pos_head_c # 当前蛇头坐标:列

  48. declare move_dir # 移动方向

  49. declare snake_len # 蛇长
  50. declare -a snake # 蛇体数据数组
  51. declare -a pos_snake_r # 每一节蛇体的坐标行值
  52. declare -a pos_snake_c # 每一节蛇体的坐标列值
  53. declare old_tail_pos_r # 移动后的蛇尾位置r
  54. declare old_tail_pos_c # 移动后的蛇尾位置c

  55. 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}" )
  56. declare -r food_style_count=${#food_style[@]}
  57. declare food
  58. declare food_pos_r
  59. declare food_pos_c
  60. declare food_style_index

  61. # ============================================================================
  62. # 函数定义
  63. # ============================================================================

  64. # 随机函数
  65. # 参数一:随机数的上限+1,缺省为 10
  66. function random()
  67. {
  68.     echo $(( RANDOM % ${1:-10} ))
  69. }

  70. # 键盘输入响应函数
  71. function Input()
  72. {
  73.     while true
  74.     do
  75.         read -s -n 1 -a key
  76.         key="${key[@]: -1}"
  77.         case $key in
  78.             $KEY_UP) sign=$SIG_UP ;;
  79.             $KEY_DOWN) sign=$SIG_DOWN ;;
  80.             $KEY_LEFT) sign=$SIG_LEFT ;;
  81.             $KEY_RIGHT) sign=$SIG_RIGHT ;;
  82.             $KEY_PAUSE) sign=$SIG_PAUSE ;;
  83.             $KEY_EXIT) sign=$SIG_EXIT ;;
  84.             *) continue ;;
  85.         esac

  86.         kill -s $sign $pid_loop

  87.         # 若是退出按键,则根据游戏循环是否存在来判断是否确认退出
  88.         if (( sign == SIG_EXIT ))
  89.         then
  90.             sleep 0.1
  91.             if ! ps -p $pid_loop > /dev/null
  92.             then
  93.                 break
  94.             fi
  95.         fi
  96.     done
  97. }

  98. # 输入动作响应函数
  99. # 输入参数一:键盘消息
  100. function Action()
  101. {
  102.     sign=$1

  103.     # 若游戏暂停,则只响应暂停信号和退出信号
  104.     if (( game_paused && sign != SIG_PAUSE && sign != SIG_EXIT ))
  105.     then
  106.         return
  107.     fi

  108.     # 输入线程的暂停处理
  109.     if (( game_overed && sign == SIG_PAUSE ))
  110.     then
  111.         return
  112.     fi

  113.     case $sign in
  114.         $SIG_UP) OnPressPlayerMove "U" ;;
  115.         $SIG_DOWN) OnPressPlayerMove "D" ;;
  116.         $SIG_LEFT) OnPressPlayerMove "L" ;;
  117.         $SIG_RIGHT) OnPressPlayerMove "R" ;;
  118.         $SIG_PAUSE) OnPressGamePause ;;
  119.         $SIG_EXIT) OnPressGameExit ;;
  120.     esac
  121. }

  122. # 系统初始化
  123. function Init()
  124. {
  125.     screen_width=$(tput cols)
  126.     screen_height=$(tput lines)

  127.     game_map_width=$GAME_AREA_WIDTH
  128.     game_map_height=$GAME_AREA_HEIGHT

  129.     # 终端设置
  130.     stty_save=$(stty -g) # 保存stty配置
  131.     stty -echo # 关闭输入回显
  132.     tput civis # 关闭光标
  133.     shopt -s nocasematch # 开启大小写case比较的开关
  134.     clear

  135.     # 有时候echo会提示中断的函数调用,目前没啥解决办法,就把错误提示屏蔽了先
  136.     exec 2> /dev/null
  137. }

  138. # 按键响应:游戏暂停
  139. function OnPressGamePause()
  140. {
  141.     game_paused=$(( ! game_paused ))
  142. }

  143. # 按键响应:退出游戏
  144. function OnPressGameExit()
  145. {
  146.     game_overed=1
  147. }

  148. # 按键响应:玩家动作
  149. # $1: U D L R = up down left right
  150. function OnPressPlayerMove()
  151. {
  152.     move_dir="$1"
  153. }

  154. function GameInit()
  155. {
  156.     # 设定输入响应函数
  157.     # trap Action $SIG_UP $SIG_DOWN $SIG_LEFT $SIG_RIGHT $SIG_PAUSE $SIG_SHOOT $SIG_EXIT
  158.     trap "Action $SIG_UP" $SIG_UP
  159.     trap "Action $SIG_DOWN" $SIG_DOWN
  160.     trap "Action $SIG_LEFT" $SIG_LEFT
  161.     trap "Action $SIG_RIGHT" $SIG_RIGHT
  162.     trap "Action $SIG_PAUSE" $SIG_PAUSE
  163.     trap "Action $SIG_EXIT" $SIG_EXIT

  164.     DrawBorder

  165.     (( pos_head_c = game_map_width / 2 + $GAME_AREA_LEFT - 1 ))
  166.     (( pos_head_r = game_map_height / 2 + $GAME_AREA_TOP - 1 ))
  167.     game_overed=0
  168.     move_dir='X'

  169.     # 测试和截图用 :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}")
  170.     snake=(H A P P Y 3 . 8)
  171.     snake_len=${#snake[*]}
  172.     for (( i = 0; i < snake_len; ++i ))
  173.     do
  174.         pos_snake_r[$i]=$pos_head_r
  175.         pos_snake_c[$i]=$(( ((pos_head_c + i + 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT ))
  176.     done

  177.     RandomFood
  178. }

  179. # 游戏循环
  180. function GameLoop()
  181. {
  182.     GameInit

  183.     while true
  184.     do
  185.         if (( game_overed ))
  186.         then
  187.             break
  188.         fi

  189.         FrameAction
  190.         sleep 0.04 # 每秒25帧,sleep 0.04
  191.     done
  192. }

  193. function GameStart()
  194. {
  195.     GameLoop &
  196.     pid_loop=$!
  197. }

  198. function FrameAction()
  199. {
  200.     # 若游戏暂停,则不进行帧动作
  201.     if (( game_paused ))
  202.     then
  203.         return
  204.     fi

  205.     MoveSnake
  206.     if CheckAndEatFood
  207.     then
  208.         RandomFood
  209.     fi

  210.     DrawSnake
  211. }

  212. # 如果有吃到食物,返回真 0,否则返回假 1
  213. function CheckAndEatFood()
  214. {
  215.     if (( pos_head_r != food_pos_r || pos_head_c != food_pos_c ))
  216.     then
  217.         return 1
  218.     fi

  219.     snake[$snake_len]=$food
  220.     pos_snake_r[$snake_len]=$old_tail_pos_r
  221.     pos_snake_c[$snake_len]=$old_tail_pos_c
  222.     (( ++snake_len ))

  223.     return 0
  224. }

  225. function MoveSnake()
  226. {
  227.     case $move_dir in
  228.         'U') (( pos_head_r = ((pos_head_r - 1 + game_map_height - $GAME_AREA_TOP) % game_map_height) + $GAME_AREA_TOP )) ;;
  229.         'D') (( pos_head_r = ((pos_head_r + 1 + game_map_height - $GAME_AREA_TOP) % game_map_height) + $GAME_AREA_TOP )) ;;
  230.         'L') (( pos_head_c = ((pos_head_c - 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT )) ;;
  231.         'R') (( pos_head_c = ((pos_head_c + 1 + game_map_width - $GAME_AREA_LEFT) % game_map_width) + $GAME_AREA_LEFT )) ;;
  232.         *) return ;;
  233.     esac

  234.     (( tail_index = snake_len - 1 ))
  235.     old_tail_pos_r=${pos_snake_r[$tail_index]}
  236.     old_tail_pos_c=${pos_snake_c[$tail_index]}

  237.     for (( i = snake_len - 1; i >= 1; --i ))
  238.     do
  239.         pos_snake_r[$i]=${pos_snake_r[$(( i - 1 ))]}
  240.         pos_snake_c[$i]=${pos_snake_c[$(( i - 1 ))]}
  241.     done

  242.     pos_snake_r[0]=$pos_head_r
  243.     pos_snake_c[0]=$pos_head_c
  244. }

  245. function DrawSnake()
  246. {
  247.     echo -ne "${ESC}[${old_tail_pos_r};${old_tail_pos_c}H "

  248.     for (( i = 0; i < snake_len; ++i ))
  249.     do
  250.         pos_r=${pos_snake_r[$i]}
  251.         pos_c=${pos_snake_c[$i]}

  252.         echo -ne "${ESC}[${pos_r};${pos_c}H${snake[$i]}"
  253.     done
  254. }

  255. function DrawSnakeXXX()
  256. {
  257.     echo -ne "${ESC}[${old_tail_pos_r};${old_tail_pos_c}H "

  258.     for (( i = 1; i < snake_len - 1; ++i ))
  259.     do
  260.         (( j = i - 1 ))
  261.         pos_r=${pos_snake_r[$j]}
  262.         pos_c=${pos_snake_c[$j]}

  263.         echo -ne "${ESC}[${pos_r};${pos_c}H${snake[$i]}"
  264.     done

  265.     echo -ne "${ESC}[${pos_head_r};${pos_head_c}H${snake[0]}"
  266. }

  267. function RandomFood()
  268. {
  269.     # 暂不考虑蛇满屏的时候无法生成新食物的情况
  270.     while true
  271.     do
  272.         (( food_pos_r = $(random game_map_height) + $GAME_AREA_TOP ))
  273.         (( food_pos_c = $(random game_map_width) + $GAME_AREA_LEFT ))

  274.         again=0
  275.         for (( i = 0; i < snake_len; ++i ))
  276.         do
  277.             if (( pos_snake_r[i] == food_pos_r && pos_snake_c[i] == food_pos_c ))
  278.             then
  279.                 again=1
  280.                 break
  281.             fi
  282.         done

  283.         if (( again == 0 ))
  284.         then
  285.             break
  286.         fi
  287.     done

  288.     food_style_index=$(random $food_style_count)
  289.     food=${food_style[$food_style_index]}
  290.     echo -ne "${ESC}[${food_pos_r};${food_pos_c}H$food"
  291. }

  292. # 退出游戏清理操作
  293. function ExitClear()
  294. {
  295.     # 恢复大小写case比较的开关
  296.     shopt -u nocasematch

  297.     # 恢复stty配置
  298.     stty $stty_save
  299.     tput cnorm

  300.     clear
  301. }

  302. # 绘制边界
  303. function DrawBorder()
  304. {
  305.     local i
  306.     local border_h
  307.     local border_v
  308.     local r
  309.     local c
  310.     local c2

  311.     border_h=""
  312.     for (( i = 0; i < GAME_AREA_WIDTH + 1; ++i ))
  313.     do
  314.         border_h="$border_h$BORDER_H"
  315.     done

  316.     # 画顶边
  317.     r=$(( GAME_AREA_TOP - 1 ))
  318.     c=$(( GAME_AREA_LEFT - 1 ))
  319.     echo -ne "${ESC}[${r};${c}H${border_h}"

  320.     # 画底边
  321.     r=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT ))
  322.     echo -ne "${ESC}[${r};${c}H${border_h}"

  323.     c2=$(( GAME_AREA_LEFT - 1 + GAME_AREA_WIDTH + 1 ))
  324.     for (( r = GAME_AREA_TOP - 1; r < GAME_AREA_TOP + GAME_AREA_HEIGHT + 1; ++r ))
  325.     do
  326.         echo -ne "${ESC}[${r};${c}H${BORDER_V}${ESC}[${r};${c2}H${BORDER_V}"
  327.     done
  328. }

  329. # 主函数
  330. function Main()
  331. {
  332.     Init

  333.     GameStart
  334.     Input
  335.     ExitClear
  336. }

  337. Main


阅读(2238) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~