前言
试试比较多动态画面的飞机游戏是否可以用 Shell 来实现。
正文
试了一次性 echo 一个大字串,或者是分批 echo 小一点的字串,发现都很卡,最后还是保留最初的方案,有需要输出屏的时候就直接 echo。也弄过一个消息队列的结构,后来改着改着,发现消息队列都用不上了。
截图如下:
代码
颜色代码头文件 colors.sh
点击(此处)折叠或打开
-
#!/bin/bash
-
# colors.sh
-
# 作者:亚丹
-
# http://seesea.blog.chinaunix.net
-
# http://blog.csdn.net/nicenight
-
# 一些输出控制常量定义,已经不只包括颜色常量了,对不起这个文件名呀
-
-
# 控制序列初始字符
-
ESC="\E"
-
-
# 前景色
-
BLK=$ESC"[30m"
-
RED=$ESC"[31m"
-
GRN=$ESC"[32m"
-
YEL=$ESC"[33m"
-
BLU=$ESC"[34m"
-
MAG=$ESC"[35m"
-
CYN=$ESC"[36m"
-
WHT=$ESC"[37m"
-
-
# 高亮前景色
-
HIR=$ESC"[1;31m"
-
HIG=$ESC"[1;32m"
-
HIY=$ESC"[1;33m"
-
HIB=$ESC"[1;34m"
-
HIM=$ESC"[1;35m"
-
HIC=$ESC"[1;36m"
-
HIW=$ESC"[1;37m"
-
-
# 背景色
-
BBLK=$ESC"[40m"
-
BRED=$ESC"[41m"
-
BGRN=$ESC"[42m"
-
BYEL=$ESC"[43m"
-
BBLU=$ESC"[44m"
-
BMAG=$ESC"[45m"
-
BCYN=$ESC"[46m"
-
BWHT=$ESC"[47m"
-
-
# 高亮背景色
-
BHIR=$ESC"[41;1m"
-
BHIG=$ESC"[42;1m"
-
BHIY=$ESC"[43;1m"
-
BHIB=$ESC"[44;1m"
-
BHIM=$ESC"[45;1m"
-
BHIC=$ESC"[46;1m"
-
BHIW=$ESC"[47;1m"
-
-
# 恢复默认显示
-
NOR=$ESC"[2;37;0m"
-
-
# 闪烁效果
-
BLINK=$ESC"[5m"
-
-
# 粗体效果
-
BOLD=$ESC"[1m"
-
-
# 反向显示
- INV=$ESC"[7m"
主程序源文件 aircraft.sh
点击(此处)折叠或打开
-
#!/bin/bash
-
# aircraft.sh
-
#
-
# 作者:亚丹
-
# 时间:2012-06-27
-
# seesea2517#gmail*com
-
# http://seesea.blog.chinaunix.net
-
# http://blog.csdn.net/nicenight
-
#
-
# 功能:飞机游戏的Demo
-
# 游戏规则:
-
# 1. 射击敌机,击中一架敌机得一分
-
# 2. 每十分升一级
-
# 3. 升级后,敌机的出现几率将会上升
-
# 4. 升级后,将增加可发射子弹的数量
-
-
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_SHOOT=SIGRTMIN+5
-
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_SHOOT="J"
-
declare -r KEY_PAUSE="P"
-
declare -r KEY_EXIT="Q"
-
-
# 游戏区域位置大小
-
declare -r GAME_AREA_TOP=10
-
declare -r GAME_AREA_LEFT=30
-
declare -r GAME_AREA_WIDTH=43
-
declare -r GAME_AREA_HEIGHT=33
-
-
# 标题位置
-
declare -r TITLE_POS_LEFT=22
-
declare -r TITLE_POS_TOP=2
-
-
# 信息显示位置
-
declare -r MSG_POS_TOP=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT - 20 ))
-
declare -r MSG_POS_LEFT=$(( GAME_AREA_LEFT + GAME_AREA_WIDTH + 10 ))
-
declare -r MSG_SCORE_TOP=$(( MSG_POS_TOP + 1 ))
-
declare -r MSG_SCORE_LEFT=$(( MSG_POS_LEFT + 16 ))
-
declare -r MSG_LEVEL_TOP=$(( MSG_POS_TOP + 2 ))
-
declare -r MSG_LEVEL_LEFT=$MSG_SCORE_LEFT
-
declare -r MSG_BULLET_TOP=$(( MSG_POS_TOP + 3 ))
-
declare -r MSG_BULLET_LEFT=$MSG_SCORE_LEFT
-
declare -r MSG_TOP_SCORE_TOP=$(( MSG_POS_TOP + 4 ))
-
declare -r MSG_TOP_SCORE_LEFT=$MSG_SCORE_LEFT
-
-
# 游戏边界显示字符(分横向和纵向两种字符)
-
declare -r BORDER_H="${BHIG} ${NOR}"
-
declare -r BORDER_V="${BHIG} ${NOR}"
-
-
# 游戏最高分存放文件
-
declare -r FILE_TOP_SCORE=".top_score"
-
-
# ============================================================================
-
# 全局常量
-
# ============================================================================
-
-
# 玩家图标 敌机图标
-
# A -+-
-
# -=#=- -=#=-
-
# -+- V
-
declare -r player_width=5 # 玩家图标的宽
-
declare -r player_height=3 # 玩家图标的高
-
declare -r player_gun_offset_c=$(( (player_width - 1) / 2 - 1 )) # 玩家枪炮的相对于坐标的偏移
-
declare -r player_gun_offset_r=-2 # 玩家枪炮的相对于坐标的偏移
-
declare -r enemy_width=5 # 敌机图标的宽
-
declare -r enemy_height=3 # 敌机图标的高
-
-
declare -r enemy_random_range_max=20 # 每帧随机产生敌机的随机数范围 20 表示 1/20 的几率
-
-
# 各种不同风格的星星集合
-
declare -ar ar_star_style=( "${RED}.${NOR}" "${GRN}.${NOR}" "${YEL}.${NOR}" "${BLU}.${NOR}" "${MAG}.${NOR}" "${CYN}.${NOR}" "${WHT}.${NOR}" "${HIR}.${NOR}" "${HIG}.${NOR}" "${HIY}.${NOR}" "${HIB}.${NOR}" "${HIM}.${NOR}" "${HIC}.${NOR}" "${HIW}.${NOR}" )
-
-
# 敌机颜色列表
-
declare -ar ar_enemy_color=( "$HIR" "$HIG" "$HIY" "$HIB" "$HIM" "$HIC" "$HIW" )
-
-
# ============================================================================
-
# 全局变量
-
# ============================================================================
-
-
declare stty_save # 终端设置
-
declare game_paused=0 # 游戏暂停标志
-
declare game_overed=0 # 游戏中止标志
-
declare game_exit_confirmed=0 # 游戏确认退出标志
-
-
declare -a ar_pos_bullet # 子弹列表
-
declare -a ar_old_pos_bullet # 子弹旧坐标列表
-
-
declare -a ar_pos_enemy # 敌机列表
-
declare -a ar_old_pos_enemy # 敌机旧坐标列表
-
-
declare -a ar_pos_star # 背景星星列表
-
declare -a ar_old_pos_star # 背景星星旧坐标列表
-
-
declare pid_loop # 消息循环的进程pid
-
-
declare screen_width # 屏宽
-
declare screen_height # 屏高
-
-
declare width_max # 游戏区转换为屏幕坐标的最大位置:宽
-
declare height_max # 游戏区转换为屏幕坐标的最大位置:高
-
declare width_min # 游戏区转换为屏幕坐标的最小位置:宽
-
declare height_min # 游戏区转换为屏幕坐标的最小位置:高
-
-
declare range_player_r_min # 玩家可移动位置的最小行
-
declare range_player_r_max # 玩家可移动位置的最大行
-
declare range_player_c_min # 玩家可移动位置的最小列
-
declare range_player_c_max # 玩家可移动位置的最大列
-
-
declare range_enemy_r_min # 敌机可移动位置的最小行
-
declare range_enemy_r_max # 敌机可移动位置的最大行
-
declare range_enemy_c_min # 敌机可移动位置的最小列
-
declare range_enemy_c_max # 敌机可移动位置的最大列
-
-
declare pos_player_r # 当前玩家坐标:行
-
declare pos_player_c # 当前玩家坐标:列
-
declare old_player_r # 先前玩家坐标:行
-
declare old_player_c # 先前玩家坐标:列
-
-
declare score=100 # 分数
-
declare score_top=0 # 最高分
-
declare level=10 # 级别
-
declare bullet_num=$level # 当前可发射的子弹数
-
declare bullet_num_max=$level # 最大可发射的子弹数
-
-
# ============================================================================
-
# 函数定义
-
# ============================================================================
-
-
# ----------------------------------------------------------------------------
-
# 通用函数
-
# ----------------------------------------------------------------------------
-
-
# 随机函数
-
# 参数一:随机数的上限+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_SHOOT) sign=$SIG_SHOOT ;;
-
$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_SHOOT) OnPressShoot ;;
-
$SIG_PAUSE) OnPressGamePause ;;
-
$SIG_EXIT) OnPressGameExit ;;
-
esac
-
}
-
-
# 系统初始化
-
function Init()
-
{
-
width_max=$(( GAME_AREA_LEFT + GAME_AREA_WIDTH ))
-
height_max=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT ))
-
width_min=$GAME_AREA_LEFT
-
height_min=$GAME_AREA_TOP
-
-
screen_width=$(tput cols)
-
screen_height=$(tput lines)
-
-
if [ $screen_width -lt $width_max -o $screen_height -lt $height_max ]
-
then
-
echo "Screen size too small (width = $screen_width, height = $screen_height), should be width = $width_max and height = $height_max at least."
-
exit 1
-
fi
-
-
range_player_r_min=$height_min
-
range_player_r_max=$(( height_max - player_height ))
-
range_player_c_min=$width_min
-
range_player_c_max=$(( width_max - player_width ))
-
-
range_enemy_r_min=$height_min
-
range_enemy_r_max=$(( height_max - enemy_height ))
-
range_enemy_c_min=$width_min
-
range_enemy_c_max=$(( width_max - enemy_width ))
-
-
game_paused=0
-
game_overed=0
-
-
# 终端设置
-
stty_save=$(stty -g) # 保存stty配置
-
stty -echo # 关闭输入回显
-
tput civis # 关闭光标
-
shopt -s nocasematch # 开启大小写case比较的开关
-
clear
-
-
# 有时候echo会提示中断的函数调用,目前没啥解决办法,就把错误提示屏蔽了先
-
exec 2> /dev/null
-
}
-
-
# 判断一个矩形物件是否在游戏区域内
-
# 参数:行、列、高、宽
-
function IsInGameArea()
-
{
-
local r c h w
-
local r_min r_max c_min c_max
-
r=$1
-
c=$2
-
h=$3
-
w=$4
-
r_min=$height_min
-
r_max=$(( height_max - h ))
-
c_min=$width_min
-
c_max=$(( width_max - w ))
-
-
if [ $r -le $r_min -o $r -ge $r_max -o $c -le $c_min -o $c -ge $c_max ]
-
then
-
return 1
-
fi
-
-
return 0
-
}
-
-
# 游戏初始化
-
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_SHOOT" $SIG_SHOOT
-
trap "Action $SIG_EXIT" $SIG_EXIT
-
-
pos_player_r=$(( range_player_r_max ))
-
pos_player_c=$(( (GAME_AREA_WIDTH - $player_width) / 2 + GAME_AREA_LEFT ))
-
-
old_player_r=$pos_player_r
-
old_player_c=$pos_player_c
-
-
game_paused=0
-
game_overed=0
-
-
TopScoreRead
-
TitleShow
-
DrawBorder
-
MessageShow
-
PlayerMove # 不带参数,就会根据当前位置刷新一下玩家
-
}
-
-
# 游戏循环
-
function GameLoop()
-
{
-
GameInit
-
-
while true
-
do
-
if (( game_exit_confirmed ))
-
then
-
break
-
fi
-
-
FrameAction
-
sleep 0.04 # 每秒25帧,sleep 0.04
-
done
-
}
-
-
# 游戏的暂停切换
-
# 切换后的状态为暂停则返回真(0),非暂停返回假(1)
-
function GamePauseSwitch()
-
{
-
game_paused=$(( ! game_paused ))
-
-
return $(( ! game_paused ))
-
}
-
-
# 启动游戏
-
function GameStart()
-
{
-
GameLoop &
-
pid_loop=$!
-
}
-
-
# 游戏结束
-
function GameOver()
-
{
-
game_paused=1
-
game_overed=1
-
TipShow "Game Over! Press $KEY_EXIT to exit."
-
}
-
-
# 退出游戏
-
function GameExit()
-
{
-
game_exit_confirmed=1
-
}
-
-
# 退出游戏清理操作
-
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
-
}
-
-
# 处理列表中的物件坐标
-
# 仅是改坐标,显示丢给显示函数处理
-
# 参数一为当前位置列表
-
# 参数二为旧位置列表
-
# 参数三四为物件高宽,缺省为 1
-
function ListMove()
-
{
-
local i flag pos
-
local r c h w
-
local ar ar_old
-
-
ar=$1
-
ar_old=$2
-
h=${3:-1}
-
w=${4:-1}
-
-
# 记录旧位置
-
eval "$ar_old=( \"\${$ar[@]}\" )"
-
-
# 更新当前位置
-
flag=0
-
eval "count=\${#$ar[@]}"
-
for (( i = 0; i < count; ++i ))
-
do
-
eval "pos=( \${$ar[$i]} )"
-
r=$(( ${pos[0]} + ${pos[2]} ))
-
c=$(( ${pos[1]} + ${pos[3]} ))
-
-
if ! IsInGameArea $r $c $h $w
-
then
-
# 超出游戏区域的删除
-
eval "unset $ar[$i]"
-
flag=1
-
else
-
# 在游戏区域内的更新位置
-
eval "$ar[$i]=\"$r $c ${pos[2]} ${pos[3]} ${pos[4]}\""
-
fi
-
done
-
-
# 如果有删除元素,则要重组数组,以便下标连续
-
if [ $flag -eq 1 ]
-
then
-
eval "$ar=( \"\${$ar[@]}\" )"
-
fi
-
}
-
-
# 读取最高分数
-
function TopScoreRead()
-
{
-
# 若没有最高分数记录则最高分为0
-
if [ ! -f "$FILE_TOP_SCORE" ]
-
then
-
score_top=0
-
return
-
fi
-
-
# 读取文件内容,若不是有效数字则设置最高分为0
-
score_top=$(cat "$FILE_TOP_SCORE")
-
if [ "${score_top//[[:digit:]]}" != "" ]
-
then
-
score_top=0
-
fi
-
}
-
-
# 保存最高分数
-
function TopScoreSave()
-
{
-
echo $score_top > "$FILE_TOP_SCORE"
-
}
-
-
# 更新最高分
-
# 最高分更改了返回真,没有更改返回假
-
function TopScoreUpdate()
-
{
-
if (( score < score_top ))
-
then
-
return 1
-
fi
-
-
score_top=$score
-
return 0
-
}
-
-
# 刷新最高分的屏幕显示
-
function TopScoreRefresh()
-
{
-
echo -ne "${ESC}[${MSG_TOP_SCORE_TOP};${MSG_TOP_SCORE_LEFT}H$score_top "
-
}
-
-
# 标题显示
-
function TitleShow()
-
{
-
local r c
-
r=$TITLE_POS_TOP
-
c=$TITLE_POS_LEFT
-
-
echo -ne "${ESC}[${r};${c}H______________________________________________________________" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H___ |___ _/__ __ \_ ____/__ __ \__ |__ ____/__ __/" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H__ /| |__ / __ /_/ / / __ /_/ /_ /| |_ /_ __ / " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H_ ___ |_/ / _ _, _// /___ _ _, _/_ ___ | __/ _ / " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H/_/ |_/___/ /_/ |_| \____/ /_/ |_| /_/ |_/_/ /_/ " ;
-
}
-
-
# 显示游戏信息
-
function MessageShow()
-
{
-
local r c
-
-
r=$MSG_POS_TOP
-
c=$MSG_POS_LEFT
-
-
echo -ne "${ESC}[${r};${c}HInfomation" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Score : $score " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Level : $level " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Bullet : $bullet_num " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Top Score : $score_top " ; (( ++r ))
-
(( ++r ))
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}HOperation" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Up : $KEY_UP" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Down : $KEY_DOWN" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Left : $KEY_LEFT" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Right : $KEY_RIGHT" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Shoot : $KEY_SHOOT" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Pause : $KEY_PAUSE" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H Quit : $KEY_EXIT" ; (( ++r ))
-
(( ++r ))
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}HIntroduction" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H 1. 1 point score per enemy" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H 2. Level up every 10 points score" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H 3. Level up to increase bullet number"
-
}
-
-
# 显示提示
-
# 参数一:提示信息的内容
-
# 简单起见,只做一行的提示
-
declare length_msg # 信息长度,用于清除提示使用
-
function TipShow()
-
{
-
local msg=${1:- }
-
local r c
-
local border_h
-
local tip_lines=3
-
-
length_msg=${#msg}
-
border_h="+--$(echo $msg | sed 's/./-/g')--+"
-
msg="| ${BLINK}$msg${NOR} |"
-
-
r=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT / 2 - tip_lines ))
-
c=$(( (GAME_AREA_WIDTH - ${#border_h}) / 2 + GAME_AREA_LEFT ))
-
-
echo -ne "${ESC}[${r};${c}H$border_h" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H$msg" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H$border_h"
-
}
-
-
# 清除提示
-
function TipClear()
-
{
-
local r c
-
local empty_line
-
local len
-
local tip_lines=3
-
-
len=$(( length_msg + 6 ))
-
empty_line=$(printf "%${len}s")
-
-
r=$(( GAME_AREA_TOP + GAME_AREA_HEIGHT / 2 - tip_lines ))
-
c=$(( (GAME_AREA_WIDTH - ${#empty_line}) / 2 + GAME_AREA_LEFT ))
-
-
echo -ne "${ESC}[${r};${c}H$empty_line" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H$empty_line" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H$empty_line"
-
}
-
-
# ----------------------------------------------------------------------------
-
# 子弹处理
-
# ----------------------------------------------------------------------------
-
-
# 向子弹链表里加入一个子弹坐标即可
-
# 增加立即显示的操作
-
# 参数:row col row_speed col_speed
-
function BulletAdd()
-
{
-
# 只在游戏区域范围内的才加入,不在的就不处理
-
if [ $1 -le $height_min -o $1 -ge $height_max -o $2 -le $width_min -o $2 -ge $width_max ]
-
then
-
return
-
fi
-
-
# 若弹药用光了,则不能发射
-
if IsBulletUsedUp
-
then
-
return
-
fi
-
-
ar_pos_bullet=( "${ar_pos_bullet[@]}" "$1 $2 $3 $4" )
-
BulletPut $1 $2
-
}
-
-
# 判断弹药是否用光
-
function IsBulletUsedUp()
-
{
-
if [ $bullet_num -le 0 ]
-
then
-
return 0
-
fi
-
-
return 1
-
}
-
-
# 子弹库存数量更新
-
function BulletNumUpdate()
-
{
-
(( bullet_num = bullet_num_max - ${#ar_pos_bullet[@]} ))
-
}
-
-
# 子弹刷新
-
function BulletRefresh()
-
{
-
BulletMove
-
BulletDisplay
-
-
BulletNumUpdate
-
BulletNumRefresh
-
}
-
-
# 移动所有的子弹
-
# 仅是改坐标,显示丢给显示函数处理
-
function BulletMove()
-
{
-
ListMove ar_pos_bullet ar_old_pos_bullet
-
-
# 以下为原内容,想想要做成一个通用的函数来替换才行,于是有了 ListMove
-
# local i flag pos
-
# local r c
-
#
-
# # 记录旧位置
-
# ar_old_pos_bullet=( "${ar_pos_bullet[@]}" )
-
#
-
# # 更新当前位置
-
# flag=0
-
# count=${#ar_pos_bullet[@]}
-
# for (( i = 0; i < count; ++i ))
-
# do
-
# pos=( ${ar_pos_bullet[$i]} )
-
# r=$(( ${pos[0]} + ${pos[2]} ))
-
# c=$(( ${pos[1]} + ${pos[3]} ))
-
#
-
# if [ $r -le $height_min -o $r -ge $height_max -o $c -le $width_min -o $c -ge $width_max ]
-
# then
-
# unset ar_pos_bullet[$i]
-
# flag=1
-
# else
-
# ar_pos_bullet[$i]="$r $c ${pos[2]} ${pos[3]}"
-
# fi
-
# done
-
#
-
# # 如果有删除元素,则要重组数组,以便下标连续
-
# if [ $flag -eq 1 ]
-
# then
-
# ar_pos_bullet=( "${ar_pos_bullet[@]}" )
-
# fi
-
}
-
-
# 显示所有的子弹
-
function BulletDisplay()
-
{
-
local pos c r
-
-
for pos in "${ar_old_pos_bullet[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
BulletClear $r $c
-
done
-
-
for pos in "${ar_pos_bullet[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
BulletPut $r $c
-
done
-
}
-
-
# 显示一个子弹
-
function BulletPut()
-
{
-
tput cup $1 $2
-
echo -n "!"
-
}
-
-
# 清除一个子弹
-
function BulletClear()
-
{
-
tput cup $1 $2
-
echo -n " "
-
}
-
-
# ----------------------------------------------------------------------------
-
# 玩家处理
-
# ----------------------------------------------------------------------------
-
-
# 玩家移动
-
# $1: U D L R = up down left right
-
function PlayerMove()
-
{
-
case "$1" in
-
'U') (( pos_player_r = (pos_player_r - 1 <= range_player_r_min ? range_player_r_min : pos_player_r - 1) )) ;;
-
'D') (( pos_player_r = (pos_player_r + 1 >= range_player_r_max ? range_player_r_max : pos_player_r + 1) )) ;;
-
'L') (( pos_player_c = (pos_player_c - 1 <= range_player_c_min ? range_player_c_min : pos_player_c - 1) )) ;;
-
'R') (( pos_player_c = (pos_player_c + 1 >= range_player_c_max ? range_player_c_max : pos_player_c + 1) )) ;;
-
esac
-
-
PlayerPosUpdate $pos_player_r $pos_player_c
-
}
-
-
# 玩家位置数据更新
-
function PlayerPosUpdate()
-
{
-
pos_player_r=$1
-
pos_player_c=$2
-
}
-
-
# 玩家图像刷新
-
function PlayerDraw()
-
{
-
PlayerClear $old_player_r $old_player_c
-
PlayerPut $pos_player_r $pos_player_c
-
-
old_player_r=$pos_player_r
-
old_player_c=$pos_player_c
-
}
-
-
# 传入坐标,放置玩家
-
function PlayerPut()
-
{
-
local r c
-
r=$1
-
c=$2
-
-
echo -ne "${ESC}[${r};${c}H A${NOR}"
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}H-=#=-${NOR}"
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}H -+-${NOR}"
-
}
-
-
# 传入坐标,清除玩家
-
function PlayerClear()
-
{
-
local r c
-
r=$1
-
c=$2
-
-
echo -ne "${ESC}[${r};${c}H "
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}H "
-
(( ++r ))
-
echo -ne "${ESC}[${r};${c}H "
-
}
-
-
# ----------------------------------------------------------------------------
-
# 敌机处理
-
# ----------------------------------------------------------------------------
-
-
# 敌机随机生成
-
function EnemyRandomGen()
-
{
-
local r c
-
local speed_v speed_h
-
local rand_range
-
local color_index color_num
-
-
# 最小 5% 几率增加敌机,等级升高一级增加 5% 几率
-
if (( level < enemy_random_range_max ))
-
then
-
rand_range=$(( enemy_random_range_max - level ))
-
else
-
rand_range=1
-
fi
-
-
# 根据几率随机确定是否需要加入敌机
-
if [ $(random $rand_range) -ne 0 ]
-
then
-
return
-
fi
-
-
color_num=${#ar_enemy_color}
-
(( r = 1 + range_enemy_r_min )) # 行坐标固定为第一行
-
(( c = $(random range_enemy_c_max) + width_min )) # 随机列坐标
-
(( speed_v = $(random 3) + 1 )) # 纵向速度为 1 -> 3
-
(( speed_h = $(random 3) - 1 )) # 横向速度为 -1 -> 1
-
color_index=$(random $color_num)
-
-
EnemyAdd $r $c $speed_v $speed_h $color_index
-
}
-
-
# 敌机列表中加入一个敌机
-
function EnemyAdd()
-
{
-
# 只在游戏区域范围内的才加入,不在的就不处理
-
if [ $1 -le $range_enemy_r_min -o $1 -ge $range_enemy_r_max -o $2 -le $range_enemy_c_min -o $2 -ge $range_enemy_c_max ]
-
then
-
return
-
fi
-
-
ar_pos_enemy=( "${ar_pos_enemy[@]}" "$1 $2 $3 $4 $5" )
-
EnemyPut $1 $2 $5
-
}
-
-
# 敌机刷新
-
function EnemyRefresh()
-
{
-
EnemyMove
-
EnemyDisplay
-
}
-
-
# 移动所有的敌机
-
function EnemyMove()
-
{
-
ListMove ar_pos_enemy ar_old_pos_enemy $enemy_height $enemy_width
-
}
-
-
# 显示所有的敌机
-
function EnemyDisplay()
-
{
-
local pos c r color_index
-
for pos in "${ar_old_pos_enemy[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
EnemyClear $r $c
-
done
-
-
for pos in "${ar_pos_enemy[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
color_index=${pos[4]}
-
EnemyPut $r $c $color_index
-
done
-
}
-
-
# 传入坐标,放置敌机
-
function EnemyPut()
-
{
-
local r c color
-
r=$1
-
c=$2
-
color=${ar_enemy_color[$3]}
-
-
echo -ne "${ESC}[${r};${c}H${color} -+-${NOR}" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H${color}-=#=-${NOR}" ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H${color} V${NOR}"
-
}
-
-
# 传入坐标,清除敌机
-
function EnemyClear()
-
{
-
local r c
-
r=$1
-
c=$2
-
-
echo -ne "${ESC}[${r};${c}H " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H " ; (( ++r ))
-
echo -ne "${ESC}[${r};${c}H "
-
}
-
-
# ----------------------------------------------------------------------------
-
# 背景处理
-
# ----------------------------------------------------------------------------
-
-
# 背景星星处理线程
-
function StarRandomGen()
-
{
-
local r c
-
local speed_v speed_h
-
local style style_num
-
-
# 80% 机率增加星星
-
if [ $(random 5) -ne 0 ]
-
then
-
return
-
fi
-
-
style_num=${#ar_star_style[@]}
-
(( r = 1 + height_min ))
-
(( c = $(random $(( width_max - 1 )) ) + width_min )) # 随机列坐标
-
(( speed_v = $(random 3) + 1 )) # 纵向速度为 1 -> 3
-
(( speed_h = 0 )) # 横向速度为 0
-
style=$(random $style_num)
-
-
StarAdd $r $c $speed_v $speed_h $style
-
}
-
-
# 星星列表中加入一个星星
-
# 第五个参数为星星的风格
-
function StarAdd()
-
{
-
# 只在游戏区域范围内的才加入,不在的就不处理
-
if [ $1 -le $height_min -o $1 -ge $height_max -o $2 -le $width_min -o $2 -ge $width_max ]
-
then
-
return
-
fi
-
-
ar_pos_star=( "${ar_pos_star[@]}" "$1 $2 $3 $4 $5" )
-
StarPut $1 $2 $5
-
}
-
-
# 星星刷新
-
function StarRefresh()
-
{
-
StarMove
-
StarDisplay
-
}
-
-
# 移动所有的星星
-
function StarMove()
-
{
-
ListMove ar_pos_star ar_old_pos_star
-
}
-
-
# 显示所有的敌机
-
function StarDisplay()
-
{
-
local pos c r
-
for pos in "${ar_old_pos_star[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
StarClear $r $c
-
done
-
-
for pos in "${ar_pos_star[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
StarPut $r $c "${pos[4]}"
-
done
-
}
-
-
# 传入坐标和风格,绘置星星
-
function StarPut()
-
{
-
local r c star
-
r=$1
-
c=$2
-
star=${ar_star_style[$3]}
-
-
echo -ne "${ESC}[${r};${c}H${star}"
-
}
-
-
# 传入坐标,清除敌机
-
function StarClear()
-
{
-
local r c
-
r=$1
-
c=$2
-
-
echo -ne "${ESC}[${r};${c}H "
-
}
-
-
# ----------------------------------------------------------------------------
-
# 计分与升级处理
-
# ----------------------------------------------------------------------------
-
-
# 增加分数
-
# 参数一:增加的分数值,缺省为 1
-
function ScoreIncrease()
-
{
-
(( score += ${1:-1} ))
-
ScoreRefresh
-
-
if (( score / 10 + 1 > level ))
-
then
-
LevelUp
-
fi
-
-
if TopScoreUpdate
-
then
-
TopScoreSave
-
TopScoreRefresh
-
fi
-
}
-
-
# 分数刷新
-
function ScoreRefresh()
-
{
-
echo -ne "${ESC}[${MSG_SCORE_TOP};${MSG_SCORE_LEFT}H$score "
-
}
-
-
# 升级
-
# 参数一:升级数值,缺省为 1
-
function LevelUp()
-
{
-
(( level += ${1:-1} ))
-
LevelRefresh
-
-
# 升级增加子弹最大数量
-
bullet_num_max=$level
-
BulletNumUpdate
-
BulletNumRefresh
-
}
-
-
# 等级刷新
-
function LevelRefresh()
-
{
-
echo -ne "${ESC}[${MSG_LEVEL_TOP};${MSG_LEVEL_LEFT}H$level "
-
}
-
-
# 子弹发射数刷新
-
function BulletNumRefresh()
-
{
-
echo -ne "${ESC}[${MSG_BULLET_TOP};${MSG_BULLET_LEFT}H$bullet_num/$bullet_num_max "
-
}
-
-
# ----------------------------------------------------------------------------
-
# 帧处理
-
# ----------------------------------------------------------------------------
-
-
# 帧动作
-
declare frame_count=0 # 全局变量代替静态变量的作用
-
function FrameAction()
-
{
-
# 若游戏暂停,则不进行帧动作
-
if (( game_paused ))
-
then
-
return
-
fi
-
-
# 碰撞检测
-
HitTest
-
if (( game_overed ))
-
then
-
return
-
fi
-
-
# 每四帧刷新背景
-
if (( frame_count % 4 == 0 ))
-
then
-
StarRefresh
-
fi
-
-
# 每两帧刷新敌机
-
if (( frame_count % 2 == 0 ))
-
then
-
EnemyRefresh
-
fi
-
-
# 每帧刷新子弹
-
BulletRefresh
-
-
# 每帧刷新角色
-
PlayerDraw
-
-
-
# 每帧随机生成敌机
-
EnemyRandomGen
-
-
# 每帧随机生成星星
-
StarRandomGen
-
-
(( ++frame_count ))
-
if (( frame_count > 10000 ))
-
then
-
frame_count=0
-
fi
-
}
-
-
# ----------------------------------------------------------------------------
-
# 碰撞处理
-
# ----------------------------------------------------------------------------
-
-
# 碰撞检测
-
function HitTest()
-
{
-
# 敌机与子弹的碰撞
-
HitTestBulletEnemy
-
-
# 敌机与玩家的碰撞
-
HitTestPlayEnemy
-
}
-
-
# 碰撞判断
-
# 参数:
-
# 1 - 4:物件 1 的行、列、高、宽
-
# 5 - 8:物件 2 的行、列、高、宽
-
function IsHit()
-
{
-
local r1 c1 h1 w1 r2 c2 h2 w2
-
-
r1=$1
-
c1=$2
-
h1=$3
-
w1=$4
-
r2=$5
-
c2=$6
-
h2=$7
-
w2=$8
-
-
# 横向无交叉,未碰撞
-
if (( (r1 <= r2 && (r1 + h1) <= r2) || (r1 >= r2 && (r2 + h2) <= r1) ))
-
then
-
return 1
-
fi
-
-
# 纵向无交叉,未碰撞
-
if (( (c1 <= c2 && (c1 + w1) <= c2) || (c1 >= c2 && (c2 + w2) <= c1) ))
-
then
-
return 1
-
fi
-
-
# 碰撞
-
return 0
-
}
-
-
# 敌机与子弹的碰撞
-
function HitTestBulletEnemy()
-
{
-
local pos1 pos2
-
local r1 c1 h1 w1 r2 c2 h2 w2
-
local i j
-
local flag_reset
-
-
h1=1
-
w1=1
-
h2=$enemy_height
-
w2=$enemy_width
-
-
flag_reset=0
-
i=0
-
for pos1 in "${ar_pos_bullet[@]}"
-
do
-
pos1=( ${pos1[@]} )
-
r1=${pos1[0]}
-
c1=${pos1[1]}
-
-
j=0
-
for pos2 in "${ar_pos_enemy[@]}"
-
do
-
pos2=( ${pos2[@]} )
-
r2=${pos2[0]}
-
c2=${pos2[1]}
-
-
if IsHit $r1 $c1 $h1 $w1 $r2 $c2 $h2 $w2
-
then
-
unset ar_pos_bullet[$i]
-
unset ar_pos_enemy[$j]
-
-
BulletClear $r1 $c1
-
EnemyClear $r2 $c2
-
-
ScoreIncrease
-
-
flag_reset=1
-
fi
-
-
(( ++j ))
-
done
-
-
(( ++i ))
-
done
-
-
# 有元素删除,重新设置数组以使得下标连续
-
if [ $flag_reset -eq 1 ]
-
then
-
ar_pos_bullet=( "${ar_pos_bullet[@]}" )
-
ar_pos_enemy=( "${ar_pos_enemy[@]}" )
-
-
return 0
-
fi
-
-
return 1
-
}
-
-
# 敌机与玩家的碰撞
-
function HitTestPlayEnemy()
-
{
-
local pos r c
-
-
for pos in "${ar_pos_enemy[@]}"
-
do
-
pos=( ${pos[@]} )
-
r=${pos[0]}
-
c=${pos[1]}
-
-
# 若敌机与玩家碰撞,则游戏结束
-
if IsHit $r $c $enemy_width $enemy_width $pos_player_r $pos_player_c $player_height $player_width
-
then
-
GameOver
-
return
-
fi
-
done
-
}
-
-
# ----------------------------------------------------------------------------
-
# 按键响应
-
# ----------------------------------------------------------------------------
-
-
# 按键响应:退出游戏
-
function OnPressGameExit()
-
{
-
# 游戏结束、暂停情况下,直接退出
-
if (( game_overed || game_paused ))
-
then
-
GameExit
-
return
-
fi
-
-
# 游戏中按下退出的话,先暂停并提示确认退出
-
game_paused=1
-
TipShow "Press $KEY_EXIT to exit, $KEY_PAUSE to continue."
-
}
-
-
# 按键响应:发射子弹
-
function OnPressShoot()
-
{
-
BulletAdd $(( pos_player_r + player_gun_offset_r )) $(( pos_player_c + player_gun_offset_c )) -1 0
-
}
-
-
# 按键响应:玩家动作
-
# $1: U D L R = up down left right
-
function OnPressPlayerMove()
-
{
-
PlayerMove $1
-
}
-
-
# 按键响应:游戏暂停
-
function OnPressGamePause()
-
{
-
local msg_paused="Game paused."
-
-
if GamePauseSwitch
-
then
-
TipShow "$msg_paused"
-
else
-
TipClear
-
game_exit_confirmed=0
-
fi
-
}
-
-
# ----------------------------------------------------------------------------
-
# 主函数
-
# ----------------------------------------------------------------------------
-
-
# 主函数
-
function Main()
-
{
-
Init
-
-
GameStart
-
Input
-
ExitClear
-
}
-
- Main
- 截图发现背景星星变得很不明显了。
-
原来代码选择python可以保留格式。