最近看了一个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

相关文章:

  • 2021-11-20
  • 2022-12-23
  • 2021-11-23
  • 2021-09-23
  • 2021-08-19
  • 2021-04-02
猜你喜欢
  • 2021-04-02
  • 2021-10-22
  • 2022-12-23
  • 2021-06-11
  • 2021-10-30
  • 2022-12-23
相关资源
相似解决方案