在Linuxbash中,上一篇介绍了一个shell脚本,它使用k、j、h和l键将单个方块向上移动、下、左、右。让我们继续讨论如何旋转单个块。执行效果具体执行效果如下:移动旋转前的截图,真正执行旋转后的截图时,默认显示一个水平的Z型正方形。Z形方块可以在框架内向左、向右、向下移动,但不能向上移动。俄罗斯方块不允许向上移动。您可以按k键在水平和垂直之间来回旋转块。脚本代码假设有一个rotateblock_one.sh脚本,具体代码内容如下。在这段代码中,几乎每一行代码都提供了详细的注释,方便阅读。本文后面也会对一些关键点进行说明,以帮助理解。#!/bin/bash#实现一个可以左右移动和旋转的方块。#方块的移动和旋转范围限制在指定的帧内。#只移动和旋转所有形状的Z形块。#下面的常量指定矩形边框的上、下、左、右边框#指定边框左侧的列数FRAME_LEFT=3#指定边框右侧的列数FRAME_RIGHT=26#指定边框顶部的行数FRAME_TOP=2#指定边框底部的行数FRAME_BOTTOM=18#下面的Z_BLOCKS数组定义了Z形块的所有形状。#给定的初始值对应水平放置的Z形方块,具体形状为:#[][]#[][]#这里用行数和列数来坐标点的方式来表示位置每个小方块。#第一个小方块的起始行号和列号均为0,作为整个方块的原点。#第二个小方块和第一个小方块在同一行,行数也为0。每个小方块显示两个字符,所以第二个小方块的起始列数为2。#第三个小方块在第一个小方块的下一行,行数为1。它的列数为2。#第四个小方块和第三个小方块在同一行,它的行数为1。它的列数是4。#用这些行数和列数加上方块的起始行数和列数得到你可以定位每个小方块#显示在哪一行和哪一列。然后就可以使用ANSI转义码来设置光标的位置了。#前8个数字对应水平的Z形方块。#后8个数字对应垂直放置的Z形块。Z_BLOCKS=(\00021214\02101220\)#上面的Z_BLOCKS数组保存了Z-的所有形状shapeedblocks.#使用displayBlockIndex变量指向当前显示的方形。#每个正方形由8个数字表示。这个值要在8的基础上递增。displayBlockIndex=0#这个值加上Z_BLOCKS数组中小方块的行数,#会指定每个小方块应该显示在哪一行。#它的初始值是下一行边框上的行数。blockLine=$((FRAME_TOP+1))#blockColumn指定要显示的整个块的起始列。#这个值加上Z_BLOCKS数组中小方块的列数,#将指定每个小方块应该显示在哪一列。#它的初始值是边框左侧列数的下一列。blockColumn=$((FRAME_LEFT+1))#显示一个矩形框作为方框移动的边界范围functionshowFrame(){#设置框字符的显示属性:高亮和反显,绿色文字,绿色背景printf"\e[1;7;32;42m"locali#使用下面的"\e[line;columnH"ANSI转义码将#光标移动到指定的行和列,然后显示相应的border边框字符。#行数增加,列数不变,边框垂直显示左右边界for((i=FRAME_TOP;i<=FRAME_BOTTOM;++i));做printf"\e[${i};${FRAME_LEFT}H|"printf"\e[${i};${FRAME_RIGHT}H|"done#列数增加,行数不变,边框上下边界水平显示for((i=FRAME_LEFT+1;i=FRAME_BOTTOM));然后返回1fiif((nextColumn<=FRAME_LEFT||nextColumn>=FRAME_RIGHT));thenreturn1fidone#遍历正方形的所有小方块,如果可以移动则返回0return0}#向左移动正方形functionleftMoveBlock(){#每次向左移动,必须移动一个小方块的距离。每个小方块占两列,#所以向左移动后,新的起始列号就是前面的第二列,后面要减2。localnewBaseColumn=$((blockColumn-2))#bash的ifstatement可以判断任何命令的返回值,不是#只能判断[,[[,((等命令的返回值。我们直接判断#canPlaceB锁函数的返回值。如果它返回0,则为真。如果canPlaceBlock"$blockLine""$newBaseColumn""$displayBlockIndex";然后#你可以移动方块。先清空原来的块drawBlock0#更新blockColumn的值,以便后面使用块显示在新的列上((blockColumn-=2))drawBlock1fi}#将块向右移动函数rightMoveBlock(){#每次向右移动,需要移动一个小方块的距离。每个小块占两列,#所以右移后新的起始列号就是后面的第二列,下面要加2。localnewBaseColumn=$((blockColumn+2))#bash的if语句可以判断任意命令的返回值,Not#只能判断[,[[,((等命令的返回值,下面直接判断#canPlaceBlock函数的返回值,如果返回0则为trueifcanPlaceBlock"$blockLine""$newBaseColumn""$displayBlockIndex";then#可以移动方块,先清除原方块drawBlock0#更新blockColumn的值,使块显示在新列上((blockColumn+=2))drawBlock1fi}#向下移动块functiondownMoveBlock(){localnewBaseLine=$((blockLine+1))ifcanPlaceBlock"$newBaseLine""$blockColumn""$displayBlockIndex";then#可以移动方块,先清空原方块drawBlock0#更新blockLine的值,使其可以放在新的行上显示方块((blockLine+=1))drawBlock1fi}#根据displayBlockIndex的值,得到下一个#要显示的块索引。需要检查新的索引是否越界,如果越界则重置为0functiongetNextBlockIndex(){localnextBlockIndex=$((displayBlockIndex+8))#${#Z_BLOCKS[@]}获取Z_BLOCKS数组中元素的数量。#当nextBlockIndex大于数组元素个数时,越界,#重置为0。从头开始旋转方块。如果[$nextBlockIndex-ge${#Z_BLOCKS[@]}];thennextBlockIndex=0fi#这里需要使用echo命令输出新的索引,这样我们就可以在外面使用#$(getNextBlockIndex)来获取这个值。#如果使用return命令用$?不方便去取回。echo"$nextBlockIndex"}#旋转方块。根据俄罗斯方块的规则,方块不能向上移动。functionrotateBlock(){localnewBlockIndex="$(getNextBlockIndex)"#旋转方块形状改变后,检查是否可以旋转ifcanPlaceBlock"$blockLine""$blockColumn""$newBlockIndex";然后#可以旋转成新的块形状。先清空原块drawBlock0#更新displayBlockIndex的值,指向旋转后下一个块的形状displayBlockIndex="$(getNextBlockIndex)"#显示一个新的块drawBlock1fi}#重置终端的显示状态为原始状态functionresetDisplay(){#将光标显示到边框底部的下一行,#这样终端提示显示在边框后面,避免混淆printf"\e[$((FRAME_BOTTOM+1));0H"#显示光标printf"\e[?25h"#将终端的字符属性重置为原来的stateprintf"\e[0m"}#初始化显示状态。例如显示边框、隐藏光标等。functioninitDisplay(){#由于方块会显示在指定的行列中,#为了避免已有内容的干扰,先清屏。clear#隐藏光标printf"\e[?25l"#显示提示字符串echo"Usage:kkey:rotateblock.j/h/lkey:moveblockdown/left/right.qkey:exit"#showframeshowFrame#首先显示一个默认的正方形drawBlock1}initDisplay#循环读取用户键并相应地处理它们whileread-s-n1char;docase"$char"in#hkey向左移动一列"h")leftMoveBlock;;#l键向右移动一列"l")rightMoveBlock;;#j键向下移动一行"j")downMoveBlock;;#k键旋转方块。俄罗斯方块不能向上移动方块"k")rotateBlock;;#q键退出"q")break;;esacdoneresetDisplayexit代码关键点说明确认旋转块的形状在rotateblock_one.sh脚本中,定义了一个Z_BLOCKS数组来存储锯齿形块的所有形状,每个形状用8个数字表示。使用displayBlockIndex变量指定要显示的块形状的第一个数字。当需要旋转到下一个形状时,将8添加到displayBlockIndex变量,指向下一个块形状的第一个数字。然后您可以使用drawBlock1语句来显示下一个块形状。检查块的位置是否超出边界在之前关于块移动的文章中,块的长度和高度是硬编码的,然后根据记录的长度和高度检查块的位置是否超出边界高度值。但是在当前的脚本中,块旋转后,块的长度和高度会发生变化。如果还是硬编码记录每个块的长高,判断起来会比较麻烦。当前脚本定义了一个canPlaceBlock函数来检查块位置是否超出边界。具体思路是根据方块移动或旋转的位置,获取每个小方块的行数和列数,然后检查小方块的显示位置是否超出边界范围。如果任何一个小方块的显示位置超出了边界范围,就意味着这个方块不能在新的位置显示。