这是转贴,不是我写的 很久以前,在高中,我从QB开始思考编程,入门后很快就学会了 汇编语言,然后,C,VB,JAVA……越来越容易。 不过QB现在已经风光不在,难得还有人提起。 以前在拨号BBS在这个时代,我给网友写了几篇文章QB但没有完成教程 -- 但 入门就够了,即使语法转到VB下面还是适用的。 (别奇怪,那时我ID叫做 Float Pointer) - 信区: PROGRAMR.CHINA [程序生活]----------------------------------- -------- 信件: 2332 日期: 26 Oct 98 11:30:37 来自: Float Pointer 已读: 是 已回 信 : 否 给: All 标记: 主题: FP的QB教程 第一篇 ---------------------------------------------------------------------- -------- 看到很多站友想学QB, 我只想写一本教材, 把我几年的经验和经验 写出来. 但从未写过教材, 怕出丑, 因此,首先在舒克本地编程 区里写了四篇试试. 其实这个教程是给的DING XUAN站友写的, 其中他 提出了很多意见, 里面的Q> A>问答也是我们讨论的一部分. 通过写教程, 我受益匪浅, 例如,当我写例子时,我发现我这个例子QB也 能做出流畅的三维动画, 以前没想过. 但我很忙, 以至于一个月后整理出前几篇文章, 第五篇到 今也未写完. 我的写作思想是用有限的例子来覆盖更多的内容, 以我自己的经验 写作而不参考前人的书, 努力生动,而不是枯燥. 但是前两章总是不满意, 怎么改也不好, 因为打基础的部分. 如果您有任何问题和意见,可以直接回复我, 但我不一定马上回来, 隔 一两周都没有. :P 咳..咳... 啊啊,咪伊伊,啊啊~ (练嗓子) ;) -0- QB有别与老式BASIC,首先,它不再是纯粹的解释BASIC,而可以编译 成EXE其次,引入模块化概念,使程序更加清晰,不易出错。 还增加了许多其他功能。 学习它可以为你将来学习其他语言奠定基础,因为QB模块化概念 与C、PASICAL各种之处,各种过程控制也相似。VB是在QB的基础 语法几乎完全相同。QB模块化扩展到简单OOP (面向对象)编程,界面完全用鼠标绘制,添加各种属性( 如颜色、标题等。),然后添加QB一个漂亮的代码出来了WINDOWS程序。 所以学习VB,QB为您打下坚实的基础。我在全国编程区见过许 多站友问一些VB其实所有的问题都是QB基础知识。 当然, 使用一些简单的小程序QB做也是最方便的, 要不然M$怎么会 在DOS里带个QBASIC. 话不多说。 首先你要有个QB编程工具。DOS命令QBASIC不,因为是 个简易版的QB,版本才1.X,尽管大多数QB的功能, 但成本无法完成 文章中写的所有工作。QB建议用4.5版是最后一个版本QB。但仍 然有BUG,例如,有些汉字不能输入。 最后,您可以通过调用系统中断输入汉字,但这很麻烦。 如你直接用MSBASIC7.X,这是DOS下最后一个BASIC,完美,能输 汉字。 另外VB FOR DOS 1.0也可以用,不画界面就成了QB。 安好QB后首先把OPTION菜单的FULL MENUS只有这样,我们才能选择一切 的功能。 -1- 让我们先编一个非常简单的小程序: 直接将以下程序输入上窗口: --- 例 1-1 SCREEN 12 FOR X=0 TO 639 LINE(0,0)-(X,479),X MOD 16 NEXT END 输入后按下F5键即可运行。怎么样?惊讶,这么漂亮! :) 这个小程序只是为了提高你对学习的兴趣,你必须自己编写,并努力工作。 :) 我们从这个小程序中学到了什么? 按下输入程序F可直接操作!假如你想编译成EXE文件,首先要 存盘(FILE然后选择菜单RUN菜单MAKE EXE FILE,选STAND-ALONE EXE 这样一个EXE文件可以运行,否则需要运行库。MAKE EXE AND EXIT后, 在命令行中输入您刚刚设置的文件名。 在QB当环境运行时,你可以随时使用它CTRL-BREAK暂停,不是因为你的 错误导致死亡。但是如果做成的话。EXE文件,CTRL-BREAK它不起作用。所以你必须这样做。 先按程序F5运行方式,调试后编译成EXE文件。 如果你用小写输入上面的程序,你会发现SCREEN,FOR,LINE,MOD,NEXT, END它自动变大写,因为这些都是系统识别的保留字,有特殊用途, 不能用作变量名。如果输入错误,系统可能会自动报告并提示 修改,比如你删除那个16试试。QB为初学者着想, 犯错不容易。 QB程序以命令为单位,每行都是一个命令,每行也可以写很多命令。 比如 SCREEN 12:FOR X=0 TO 639:... 显然,这不利于阅读。 按F6键可以切换到下面那个Immediate在那个窗口,窗户被称为立即窗 口输入 PRINT 234*10 然后你可以在屏幕上看到结果。如果看不清楚,按下F可以再看一遍。 也就是说,在这个窗口输入的任何命令都是立即执行的,不需要按压F5的, 这为调试程序提供了极大的便利。 在DEBUG菜单中有一个完美的调试工具。如果你的程序一次成功,你的程序就不会成功 有BUG,可以用QB强大的调试功能进行调试。 WATCH WINDOWS可以显示 你所要知道的变量,break point断点可以在您指定的地方自动停止 来。F8和F10可以单步操作程序,区别在于F10跳过过程函数(以后再说) 其他菜单你自己想想就知道用什么了. 最后,QB帮助系统非常完善,只要将光标移到您要检查的命令上,例如 SCREEN,按F1可以看到关于SCREEN各种用法、细节和用例。 也可以从HELP的INDEX在里面找到。习惯读书HELP编程任何语言都很好 使用,我只能带你开始,成为一名大师,无论语言学习都要仔细研究HELP。 有空可以把QB那些些EXAMPLE运行一次,这样你就可以彻底了 懂得这些命令如何使用了。 下面列出各种热键: F1 帮助 ALT-F1 最后一个帮助主题 F2 列表模块(过程和函数) F3 查找下一个 F4 看运行结果 F5 运行 SHIFT-F5 重新运行 F6 切换窗口 F7 运行程序到当前光标处 F8 简单的单步操作 F9 断点设置 SHIFT-F9 增加表达式WATCH窗户可以直接调试WATCH窗口里看 到这个表达结果. F10 但是自动跳过过程(函数) SHIFT-F8 跟踪运行历史 SHIFT-F10 跟踪运行历史 CTRL-BREAK暂停运行 虽然可以从菜单中选择, 很快你就会发现这些热键非常有用和常用, 即使你不需要死背. -2- 我们开始真正进入这部分QB语法学习。打基础很无聊,因为没意思 程序,那么多概念要硬记。但是打不好基础就学不好。 鉴于读者有一些编程概念,我不会详细说明什么是变量,什么是函数,什么是命令 了。 :) 以下内容一开始可能不容易理解, 也很枯燥, 不用细看, 有些印象是, 因为它们总是在编程中使用, 使用时可以现在查, 检查一两次就记住了. ;) ☆QB的数据表示 --- 数字表示: 在QB里面数字的表达和平时一样,可以使用负号、小数点、小数点前0 省略比如.1就是0.1。 整数八进制表示法是在开头写的&,八进制长整数123457 就是 &1234567& (后面的&表示长整数, 见后面) 现在很少使用8进制数。. 十六进制表示法是在数字之前添加的 &H,这很常用. 例如,我们需要将16进制数转换为10进制,并在立即窗口输入: ? &HFF 输出结果: 255 由于在计算机内部常以16进制运算, 我希望你能清楚地了解16进制数. 比如&hFF是字节内存表示的最大数字, 因为再加1等于&H100. 两个字节是 &H100 * &H100 -1 = 65536 -1=65535 除以2,因为有正负之分, 等于32767 32768的和. 现在你应该知道了 为什么整数的范围是 32767 到 -32768了吧? 同样的, 四个字节表示长整数, 你能自己算多大范围?. :) 可惜QB没有二进制数, 没有大多数语言. 带小数的浮点数只能用10进制.浮点表示法是科学计数法, 单精度用E表示指数: 3.14E 5 就是 3.14*10^5 (10的5次方) D表示双精度: -3.13e-20 就等于 -3*10^(-20) (10负20次方) --- QB的数据类型 数据类型是程序的灵魂,大学里有一门叫做数据结构的特殊课程 无聊的课,但很重要。 :) 在数据库语言中,数据类型是由应用程序决定的,QB其他语言的基本数据类型是 由硬件决定。数字似乎是数学中唯一的变量, 计算机由硬件决定 语言必须使用不同的类型, 它们都很有用,不同的类型会有不同的用途和效果. QB数据类型如下: 类型 后缀符号 简称 名称 长度(字节) 范围 举例 整型 % INT INTEGER 2 -32767 A% 长整型 & LNG LONG 4 约 -20亿 A& 浮点 ! SNG SINGLE 4 见下 A! 长浮点 # DBL DOUBLE 8 见下 A# 字符串 $ STR STRING ? 32767字符 A$ VB还增加了一种商业数据类型,后缀是@,两位小数。这是因为浮点类型会有误差, 可能出现0.02-0.01=0.009999的情况. 对于绘图运算这没什么, 对于商业运算就很危险了. 对于QB你可以用整型或长整型替代, 以分为单位就成了. BTW:&读做“俺的”,$读做“刀乐”,@读做“爱特” ;) 注意:浮点数与其他语言不同,不是FLOAT。 此外还有定长字符串和自定义类型。 变量名可以用任何一个非保留字的字母和数字组合,必须以字母开头。 保留字就是QB的命令和函数已经用了的单词。 大小写都没关系, QB会自动统一大小写。 注意不能用"_",QB不支持。VB等支持, 但有特别用途。 变量定义只要在变量名后加上后缀就可以指定变量类型,比如a%。 注意 a%和a$是两个不同的变量,可以同时使用。 如果不加任何后缀,系统默认是浮点类型的。 数字也是如此, 23%表示整数23, 23!代表浮点数23 当然 23.0 也是浮点数. 使用DEF后紧跟“简称”可以改变系统默认类型。 比如DEFINT A 代表所有以A开头的变量都是整型变量。 比如DEFLNG A-Z 代表所有变量(从A到Z开头)都是长整型变量。 此外还可以用DIM命令+名 AS +类型名称 指定变量类型: DIM A AS INTEGER 则A是整型变量。 --- 数据类型都是有范围的: 最大 最小 INTEGERS 32,767 -32,768 LONG 2,147,483,647 -2,147,483,648 SINGLE (7位数字) ?.402823 e+38 ?.401298 e-45 DOUBLE (15位数字) ?.7976931 d+308 ?.940656 d-324 字符串 32767个字符 0 如果所计算出的数据超过数据类型所允许的范围则会发生溢出错误。 看到这里你可能会问,如果我们使用DOUBLE类型不就全解决问题了么? 由于处理范围越大需要更多的内存去处理,速度也就越慢。 而浮点数的处理速度是永远比不上整数的(即使你用的是原装奔腾)。 因此占内存最小的整型(2BYTE)速度最快,而且能满足一般的应用, 我们一般在程序第一行加上DEFINT A-Z,这样可以成倍的提高运行速度。 数字也可指定类型,默认是整型的。如果不指定可能会出错, 看下面这个例子,在立即窗里输入: ? 32767 * 2 结果出现一个OVERFLOW(溢出)错误。如果用: ? 32767& * 2 就能输出正确的结果了。 --- 字符串 普通字符串长度是任意可变的,比如a$="asdfasdf":a$=""(空串) 字符串以两个双引号括起来。如果有引号或回车等其他字符怎么办? 可以用CHR$函数,比如CHR$(13)代表回车, 13是回车的ASCII值。 字符串可以用加法合并: ?"asdfasdf"+chr$(34) 输出: asdfasdf" --- 字符串还有定长字符串,长度由DIM命令指定,赋值时如果不够长度 用空格填补,超过长度自动截断,这类似数据库的C型字段,也正是 为便于处理数据库而设计的。比如: DIM S AS STRING*10 则S字符串的长度永远为10 在这里先说一下注释命令REM或"'"。凡是以这两个开头的代表是注释 而不是程序,QB不会执行。比如: REM asdsf sdfgdf sdfgg QB不会说这行打错了 ' PRINT 1345234 随便你怎么打都没关系,QB不执行这行 用这个命令(两种写法都一样)可以注释你的程序是干什么用的, 以免以后忘了,也可以在调试时不暂时不用的命令用"'"注释掉。 --- 数组类型. QB的数组用DIM定义,比如: DIM ARR(12,10) AS LONG 表示一个有13行,每行11个长整型数的数组(矩阵). 数组最小下标是(0,0),最大是(12,10) (这与C语言不同,最大下标多一) 最小下标可以这样定义: DIM ARR(1 TO 12,10 TO 20) 则最小下标就是arr(1,10)了. 注意,arr和arr(0,0)是两个没有关系的变量,可以同时用,不象C语言. --- QB里没有逻辑型数据 任何数字类型都可代替逻辑值.用0代表假,-1代表真,因为-1的二进制 表达法是 11111....,正好与0000....相对应,任何数据类型都是这样. 其实在判断语句里,非0就代表"真"了. 比如在立即窗里输入: IF 10 THEN ?"TRUE" ... 利用立即窗你可以实验各种命令如何使用. --- 常量 除了变量和数字以外,QB还支持常量,用CONST表示: CONST PI#=3.14159265358979323846 以后就可以用pi#表示这么长串数字了,但不能赋值。 CONST后面的表达式可以是四则运算,但不能有函数运算,比如: CONST PI2#=PI#*2 'PI#也是常数,所以能在这里使用 讲了这么多,你现在真正能看到效果的只有前面那个画图的程序。 好象很无聊,打基础嘛,我已经尽力使之做到“有聊”了。 :) 因为这些你现在用不着,以后就很常用了,也不用硬记,用的时 候可以把这封再复习一遍。 啊~~好累,今天到这里,就到这里吧。 :)
主题: FP的QB教程 第二篇 ---------------------------------------------------------------------- -------- 在写教材时发现大家都喜欢实例教学,似乎直接把我的例子抄过 去学得最快最轻松. 因此我就多多举例. 不过读者最好购买一本QB的书,看附录 部分有详细的命令和函数的列表的那种就成,因为光举例子不可能 覆盖全面.如果你的英文还可以,也可以看QB的HELP,非常详细, 而且几乎每个例子都有一个example. 第一节其实还少说了点东西, 因为我太困忘了写. :) --- 自定义数据类型: 象C语言的struct结构,QB也能用TYPE命令自己创造数据类型。如: TYPE student '定义一个“学生”类型 sname AS STRING*8 'NAME是保留字不能用 age AS INTEGER score as integer END TYPE 在TYPE定义里不能有不定长字符串,只能用定长字符串。 TYPE的作用正是处理数据库!看上面这段定义象不象数据库的字段名? 如何打开和处理库文件我们以后再说。 定义完怎样使用呢?用DIM定义变量,用 变量名.字段名 引用: DIM stu as student stu.sname="小芳" 下面看一些例子: 例 2-1 --- ==================================== DEFINT A-Z TYPE score sname AS STRING * 8 chinese AS SINGLE math AS SINGLE english AS SINGLE END TYPE DIM a(10) AS score i = 0 DO 'DO循环开始 INPUT "姓名"; a(i).sname INPUT "分数:", a(i).chinese, a(i).math, a(i).english i = i + 1 LOOP UNTIL LTRIM$(a(i - 1).sname) = "" '>LTRIM$函数是去掉字符串的前(LEFT)空格,因为sname是定长字符串,输入空 '>字符串也有8个空格. LOOP UNTIL表示循环直到后面表达式的值为"真". FOR j = i - 2 TO 0 STEP -1 'FOR循环从I-2到0,"步长"每次减一. PRINT j; ":"; a(j).sname, a(j).chinese, a(j).math, a(j).english NEXT --- ==================================== 按F5运行后先输入姓名,再输入分数,三个分数用逗号分割,必须写 三个数或输入空的逗号,否则会出错继续让你重新输入. 当不输入名字直接回车则停止,但还要输入两个逗号.最后按反序在屏幕上输出. 你会发现第一个INPUT自动加问号而第二个没有,因为第一个INPUT后跟一个分号, 而第二个后跟逗号. ※再看PRINT命令,PRINT命令也可用问号代替.在变量J和";",a(j).sname间 用分号连接,打印出来的内容就是紧挨着的,而后面用逗号连接, QB自动按制表位置放置,每隔八个字符才打印一组数字. 而在每个PRINT命令行的最后,如果没有任何符号就会自动换行, 你甚至可以用一个空的PRINT表示多换一行。如果后面有分号或逗号, 那会影响到下一个PRINT命令的位置。 --- ☆ 表达式 QB的数学运算符有: 加+,减- ,乘* ,除 /,整除 /(截断小数部分), 取模 MOD, 乘方^. 比如上次我们那个画图的程序,循环由0到639,可颜色数只有16种(0-15) 因此我们用MOD运算.所谓取模就是求余数. "怎么没有开方呀?" 有啊, X^.5 不就是对X开方么?(X的0.5次方) 怎样取倒数取平方也知道了吧,不过乘方的速度很慢,二次方最好用 X*X,取倒数最好用 1/x。 此外还有函数,跟数学上的函数概念差不多,比如在立即窗里输入: ? SIN(30) 哎?怎么不对呀?哦,原来用的是弧度,应该是 ? sin(3.141592653589793*30/180) 'PI我背过50位呢。 ;) 运算优先级跟数学一样,括号优先,先函数,再乘方,先乘除, 然后MOD,再加减 QB里很重要的一种运算是逻辑运算。 1.关系运算: = 等于,当在赋值表达式里“=”表示赋值,但在表达 式里代表判断是否等于,等于则为真(-1),否则为假(0) > 大于 < 小于 >= 大于等于 <= 小于等于 <> 不等于 关系运算可以比较数字或字符串。 字符串以ASCII值比较大小。 还记得上一篇所说的QB没有逻辑类型么? 试试 ? 1=3 有什么结果. 你可以 bool=1=2 结果bool得-1, 而bool可以是int,long甚至double类型 2.逻辑运算符 在QB里,逻辑运算符实际上是对变量逐位进行二进制运算的,有逻 辑运算和逻辑判断双重的作用。 逻辑操作符在数学上称做"布尔运算符"(boolean operator) QB的HELP 里有关于它们的“真值表” NOT 取非 AND 与 OR 或 XOR 异或 EQV 等价(同或) IMP 蕴涵 前三个我就不说了,顾名思义么。 如果c=a XOR b,则当a=b时c得0,不相等为1 如果c=a EQV b,则..........1,........0 ....... IMP ......a=1,b=0时c=0,否则c=1 如果A,B每个二进制位都是0或1,即A和B都为真(-1)或假(0), 那它的值必然也是-1(真)或0(假). 如果不为0或-1这两个数 则要用二进制一位一位的算(所有逻辑运算都是这样) 比如 4(100) xor 5(101) =1(001) (括号里是二进制) QB没有逻辑类型,我们通常用这个方法定真假: CONST False=0 CONST True=NOT false '不假可不就是真么。 ;) 只有关系运算才返回的肯定是0或-1,其他逻辑运算除了值是0或-1,否则 一般都不是返回0、-1,而是按位运算的. 下面这是个把十进制数字转换成二进制数字的程序: 例 2-2 --- ==================================== DEFINT A-Z INPUT a FOR i = 15 TO 0 STEP -1 'INT是16位整数,二进制高位在前,因此倒序循环16 次 IF a AND 2 ^ i THEN '2的i次方就是二进制数第i位等于1的情况 PRINT "1"; ELSE PRINT "0"; END IF NEXT '下一次循环 PRINT '多换一行,否则下次运行时不好看,不信你把这行去掉试试。 --- ==================================== 上面用了分行的IF命令,格式是 IF 表达式 THEN 当为真(非0)时许多许多命令 ELSE 当为假(等于0)时许多许多命令 END IF 也就是说IF a AND 2 ^ i THEN...和IF NOT((a AND 2 ^ i)=0) THEN 是等价的。 我们可以反复嵌套,比如 IF .... THEN IF ... THEN IF.... ...... END IF END IF END IF 注意IF和END IF必须配对,否则会报错,因此最好象我这样用缩进格式。 包括循环语句也是,循环也可嵌套,为保证配对也最好用缩进格式。 Q> 这些都是你自已想出来的还是书上说的,你给 Q> 记住了.要是我自已想,哪想的出来啊. :((( Q> 这是不是就叫算法?我现在也没完全看明白. Q> 这可怎么学呀,一个一个的背?不是办法吧? :((( A>当然是我自己编的了.我的教程里没有完全抄袭的程序,最多抄抄公式 A>和算法. :) A>可以说这就是算法吧,编程需要较好的数学能力.比如从10进制转成二进制 A>的公式是用那个数反复除以2,把余数倒过来写,但这种方法只适用于手工计算. A>二进制的特点是第0位表示0或1,第1位表示0或2,第三位表示0或4...第N位 A>表示0或2的N次方. A>2^i正是代表要查的位,比如2^3=8,二进制是1000,AND是位运算,只有两边都是1 A>结果才也是1,比如二进制 10100 and 1000 =0 说明第三位是0, A>如果11010 AND 1000 = 1000 不等于0 说明第三位是1. 在这里提一个系统函数:COMMAND$,没有任何参数。 它是命令行参数字符串。假如把上面这个程序的INPUT A换成 a=VAL(COMMAND$),VAL函数是把字符串转换成数字。 然后编译成EXE文件,假如叫做bin.exe,运行时打: bin 2334 (回车) 你就能看到结果了,好象DOS命令一样,COOL吧。 :) 当然,不编译成EXE也可以看到效果的,在RUN菜单有一项可以修改COMMAND$ 的值。 字符串运算只有加法,就是简单的把两个字符串连接在一起。 但QB的字符串函数非常丰富.比如下面这些常用函数: 下面都用实例来描述,直接在立即窗里输入就能看到效果. VAL: s$="3.1e-2": ? VAL(s$) 把S$字符串转换成数字变量.输出结果: .032 STR$: ?STR$(23) 把数字转换成字符串.是VAL的反函数. CHR$ ?chr$(7) 把数字转成相应的ASCII字符. ASC() ?asc("a") 把字符转成ASCII码,是CHR$的反函数 LEN LEN("12345") 返回字符长度。其实这个LEN可以返回任何变量的长度,包括自定义的TYPE。 在数据库读写中非常有用。 INSTR ?INSTR(1,"Ms Qbasic","basic") 搜索字符串,第一个数字是起始位置,可以省略。 如果返回0则是没找到。 UCASE$ ?UCASE$("DdFd") 全改大写字母 LCASE$全改小写 LTRIM$去前导空格,RTRIM$去后面的空格。前面在例 2-1里已经说过。 HEX$是把十进制数转换成16进制字符串,比如 ?HEX$(255) 显示 FF OCT$是................8 .......... BIN$...没有这个函数啦,要你自己来编才成. ;) 发现了么?凡是反回字符串的函数总有个$,返回数字的就没有. 以后我们还能自己编函数和命令呢. -3- 有了上面那些枯燥无味的基础,这一章我们就可以讨论点具体的了。 下面这个程序是个秒表,按任意键结束。 例 2-3 --- ==================================== t1 = TIMER CLS '清屏 COLOR 14, 1 '设置字符色为14(黄色),背景是1(兰色) WHILE (INKEY$ = "") LOCATE 10, 20 '把光标移到 第10行,第20个字符的位置 t2 = TIMER IF t2 < t1 THEN t1 = t1 - 24 * 60 * 60 PRINT t2 - t1 WEND --- ============ TIMER函数是从午夜零点到现在的秒数,用来计时。 好象只要把当前时间与开始时间相减就成了,可是假如计时中 到了零点(咱们网友的习惯)怎么办? IF语句判断是否过0点,如果过了就把开始时间减去一整天的时间(秒)。 这里的IF语句用的是单行IF命令。格式是: IF 表达式 THEN 为真时命令 ELSE 为假时命令 其中的命令可以用冒号分隔使用多个命令。 INKEY$函数是我最喜欢的输入命令,它运行时读键盘缓冲区,如果没有字符 则返回空( "" 或者CHR$(0) ),有则返回字符. 如果是字母/数字返回的是ASCII码(可以查QB的HELP的CONTANTS有ASCII表) 需要知道的是,ESC是ASCII 27,BACKSPACE是8,ENTER是13,TAB是9, 如果PRINT CHR$(7)就会"滴"的一声响. 如果是控制键(比如光标键,PAGEUP等),返回的是两个字符长的字符串. 第一个是空(CHR$(0) ),第二个是IBM键盘扩展ASCII码,在HELP的 CONTANTS部分有KEYBOARD SCAN CODE表,键盘扫描码基本跟扩展ASC码一样. 比如你想判断输入的字符是否是键: IF INKEY$=CHR$(0)+CHR$(72) THEN... 很遗憾的是键盘扫描码不等于扩展ASCII码,QB里也没有,我的书里有. 如果你不知道可以编个程序一个一个实验各个键的码. 可以看出,INKEY$执行时不等待直接返回的这种性质可以编出象绘图, 动画控制,游戏等即时控制程序,非常有用. 关于LOCATE命令的格式是 LOCATE 行,列,光标是否可见,光标大小起始行,光标大小结束行 后面的都可以省略,尤其光标大小,很多显示卡都不支持。 而COLOR命令本还有一个参数是设置边框色,但QB有BUG,其实也不支持。 具体颜色代码请读者自己编个程序查查看,用两个FOR循环就成了 范围0-15 Q> t1 = t1 - 24 * 60 * 60 Q> 还有这句,我凭自已想很难想出来这么写. Q> 是不是只能凭经验了? A>当然不是,要经过你的思考.如何把生活中的事物表达成函数式也是一种能力. A>就好象你把物理的应用问题转换成数学方程一样. A>上面这个数是这样算出来的: A>一天24小时,乘以一小时60分钟,乘以一分钟60秒. :) A>有空给你看一个"万年历"的程序,非常著名.它的难点是把格里历转换 A>成公式,用BASIC居然一行就能完成了. A>我以前编过一个叫BT(BatTool)的小软件, 里面就附带这个万年历程序. 下面就介绍一个比较实用的程序 - 破除SHAREWARE时间限制。 以前ZHANG TING前辈介绍过用QBASIC绕过CCDOS97时间限制的 方法。 实现的方法很多,下面是其中一种,可能与ZT的有点不同: 例2-4 --- ===========SAVEDATE.BAS ============ PRINT "DATE "; PRINT DATE$ PRINT "DEL UPDATE.BAT" DATE$="06-01-1997" '假设初次安装日期是97年6月1日 SYSTEM --- ============CC.BAT================== QBASIC /RUN SAVEDATE.BAS > UPDATE.BAT CCDOS rem 这里放输入法, 比如TWABC UPDATE --- ==================================== 实际运行时运行CC.BAT就成了 BTW:此程序我未试过,我支持国产正版软件,至今仍用东方快车版。 :) 我想运行完应该给一个Batch file missing或者“批处理文件丢失” 的错误信息,但无关紧要。 上面这个例子介绍了DATE$函数和DATE$命令(名字都一样),类似的 还有TIME$函数和命令。 DATE$是个"违反常规"的函数,既可以取出日期,也可赋值修改日期. DATE$="日期" 在QB的HELP里规定为"语句"而不是函数, 返回DATE$则规定为"日期函数". TIME$函数你自己试试就知道了. SYSTEM命令如果你用QBASIC或QB启动再OPEN程序运行,就跟END命令一样, 如果你在启动时用 QB /RUN XXX.BAS启动会自动运行BAS程序而不进入编辑界面. 而此时SYSTEM则能自动退出而不返回QB的编辑界面. 也就是说用上面的方法你用不着把程序编译成EXE也能执行,这也是 老式BASIC的运行方式. 上面那个程序如果编译成.EXE文件速度会快一些, 而且屏幕不会闪一下. DOS命令QBASIC虽然只有QB的部分功能,但也能做不少事。 比如我的BWTE的前身就是用QBASIC+批处理的办法。 --- 现在复习一下分支语句。 QB的分支语句有IF,ON... GOTO和SELECT三种,最简单的IF语句是 IF 表达式 THEN 行号 行号有两种,一种是数字,比如 x=1:y=1 IF x=y then 20 END 20 ?20 但这个数字的大小是没有意义的,跟GWBASIC不同。 QB推荐使用字母行标号,与其他语言一样,比如 x=1:y=1 IF x=y then GOTO Label END Label: ? 20 不过GOTO命令最好不要单独使用,结构化编程不使用GOTO 连上面那样的转向语句最好也不用,直接用分行IF...END IF最好。 既然GOTO不好,ON...GOTO也就不说啦. :) 现在只有SELECT没有讲过了.看下面这个例子,可以实现菜单操作: 例2-5 --- ================ ?"========================" ?" 0,E.什么都不干" ?" 1.列目录" ?" 2.改名" ?" 3,4,5.删除" ?"========================" DO A$=INPUT$(1) '从键盘读一个字符. 所谓"读"对于计算机来说就是输入 啦. SELECT CASE A$ CASE "0","E" END CASE "1" SHELL "DIR /P" CASE "2" NAME "C:/AUTOEXEC.BAT" AS "A.BAK" CASE "3" TO "5" KILL "C:/COMMAND.COM" CASE IS > "Z" ?"输了个小写字母?" CASE ELSE ?"输入错误" END SELECT LOOP '没有任何条件参数的DO...LOOP循环是死循环. --- ================ SELECT命令的条件除了可以用取值列表、取值范围外,还可以用IS变量, IS代表初始条件值,就是A$啦。 INPUT$函数需要一个整数的参数,表示输入几个字符. 它直接读指定个数字的符,而且不在屏幕上显示. END命令是结束命令,但可以省略,这样程序就由第一行运行到最后一行. 类似SYSTEM命令, 但不会自动退出. SHELL命令是调用DOS命令. 如果没有参数就可以进入DOS方式,但QB程序 仍然在内存里,比如你在立即窗里输入SHELL,然后打MEM/C/P看看内存还剩多少. 返回是用DOS命令EXIT.如果有参数,当运行完参数里的程序后将会自动 返回. NAME...AS命令相当于DOS命令REN,不过你可不要真的把AUTOEXEC.BAT给改没了. ;) KILL命令相当于DOS命令DEL. 哦,对了,仔细看看这个程序再运行, 可不要运行完了就启动不了计算机了. ;) --- 最后,再总结一下QB的输入命令: INPUT命令,想想分号和逗号的区别。如果INPUT后跟很多变量, 必须用逗号分隔每个变量。而且如果输入的是字符串,不能有逗号。 所以如果输入一行东西不如使用LINE INPUT。比如: LINE INPUT A$ 你可以输入任何字符,直到输入回车为止。当然CTRL-C和CTRL-BREAK不成。 a$=INPUT$(n) 函数是从键盘缓冲区里直接读n个字符,送到a$里, 如果A$=INPUT$(1)就相当于PRESS ANY KEY TO CONTINUE...了。 ( Which key is "ANY KEY"? ;) ) INKEY$函数每次都不等待直接从键盘缓冲区里读一个键,而且可以是 光标控制键。如果用户没有按键直接返回空字符。 无论是哪一种输入方法,QB都可能有汉字不能输入,比如“金”, 这是QB的BUG,如果你用的是MSBASIC 7就能解决问题了。 上面这些囫囵吞枣的灌给大家,恐怕记不住多少,应该 把所有例子都运行一遍,有点印象就好,编的时候再查书。 下次我就讲最吸引人的图形&动画. :)
今天我们讲最吸引人的图形&动画....慢着,先钓钓胃口. ;) 我们先复习一下QB的循环命令: FOR...NEXT循环是定次数的循环. FOR 变量=初值 TO 终值 STEP 步长 STEP步长可省略,默认是1,就是每循环一次加一. 循环的结尾用NEXT表示,我们通常用缩进格式防止FOR...NEXT不配对. 比如 FOR I=A TO B FOR J=C TO I STEP 10 .... NEXT FOR... NEXT NEXT 在QB里也支持象老式BASIC那样的NEXT I,J,K...一次代表多个FOR结束, 但不推荐这样,容易出错. 不定次循环最简单的是WHILE .... WEND,称做"当循环".比如下面是个表: --- =============== WHILE INKEY$="" LOCATE 10,20 ? time$ WEND --- =============== 当INKEY$函数为空(没有按键盘),就继续循环,否则就跳到WEND后面 QB新加的循环是DO...LOOP循环. 如果光是 do locate 10,20 ?time$ loop 那永远也退不出来,只能按CTRL-BREAK终止. 所以要加上终止条件,有WHILE和UNTIL两种,WHILE是当后面的条件为真 循环,UNTIL则正好相反,直到后面为真才退出. 可以加在DO后也可 加在LOOP后. 比如 DO WHILE INKEY$="" LOCATE 10,20 ? time$ LOOP 就跟第一个例子一模一样了. 如果加在LOOP后面有什么区别呢? 看下面这个例子: I=0 DO ?"进入循环" LOOP UNTIL I=0 如果你把UNTIL I=0加在DO的后面试试看. 也就是说,LOOP UNTIL(WHILE)无论什么条件都要执行循环体至少一次. 还有一种终止循环的方法,EXIT命令. 在循环体内部的任何地方, 只要用EXIT DO就能退出DO循环,跳到LOOP的后面. 此外也可以用EXIT FOR退出FOR循环. ... 注意没有EXIT WHILE. 呼~该说图形了。 想当初刚学会BASIC,刚用到新PC机,发现居然只能用4种颜色320X200 的分辨率,连苹果机都不如,可明明波斯王子却画得那么漂亮, .......就不忆苦思甜了。 ;) 就在那个分辨率下,我的BASICA书里居然做出来各种漂亮的二维三维 图形和动画,下面有些例子就是从那里抄的,改成VGA的分辨率。 先说一些计算机图形的知识。 显示卡可以以很多工作方式显示图象, 每一种叫做一种显示模式,有些高手也能自己创造新的显示模式。 每种显示模式有不同的分辨率,发色数,配色数,这我们在用WIN95 时就很清楚了。显示模式主要分为文本模式和图形模式两大类。 我们只介绍VGA兼容模式。QB用SCREEN 命令设置显示模式。 以前我们都用的是文本模式3,80X25字符,16色前/背景。 用QB表示出来就是 SCREEN 0 'QB里称做模式0,是唯一的文本模式 WIDTH 80 '设置宽度为80,也可设置成40。一般可以省略 如果在其他模式画完图不运行上面这两行,退出程序后可能会很不好看。 但一般来说QB会恢复默认模式的. 由于过去的技术落后,发色数和分辨率不能两全。QB也是如此. 本文只介绍QB的三种模式: SCREEN 9:640X350 X16配色器,64个发色数,双屏幕页,用于屏幕页动画 可用于EGA显示器。 SCREEN 12:640X480 X16配色器,64的三次方发色数,是日本RPG常用的模式。 SCREEN 13:320X200 X256配色器,64的三次方发色数,是C&C的显示模式。 :) 在96年以前,绝大多数游戏都用模式13,系统模式是13H,是用颜色换 分辨率的一种手段,也是“金山影霸”比当时的XING1.3要快的原因。 什么叫做配色器呢?看下面这个程序: ?"TEST" FOR I=0 TO 63 '文本模式只有64种发色数 PALETTE 7,I NEXT 大家知道计算机是用显存保存图象、图形信息的,在相同分辨率下, 表示的颜色越多需要的内存也越多。 比如640列真彩色需要640x480x256^3/8字节的内存,是个相当大的数字, 过去的CPU不能承受,因此用配色器的办法,存在显存里的只是颜色属性, 显示卡自动从颜色属性代表的配色器里取出真正的颜色(真彩色), 再显示在屏幕上。 这种方法不仅提高了速度,而且为编程者提供了一种编程方法,只要 改动一个配色器的值就可以把屏幕上所有此属性的象素该成那种颜色, WIN95的启动画面就是用这种方法做的“动画”。 我们整理一下名词定义: --- 属性:存在显存里的数值,画图时直接用,比如以前我们见过的 COLOR命令,后面跟的参数就是属性,而不是真正的颜色,真正的颜色 取决于: --- 配色器:每个属性都对应一个配色器,其多少是由显示模式决定的。 里面保存的是此属性真正的颜色代码。用 PALETTE命令改变。 这里沿用EGA的名称,在VGA硬件里称做DAC寄存器。SVGA通常有24BIT DAC 就是所谓24比特真彩色了。 --- 最大发色数:表示真正的颜色代码的最大值,取决于显示模式。 当然,在SCREEN换模式时系统会自动把各配色器置默认值,所以通常 我们总把属性0当作黑色,其实用PALETTE 0,X可以把背景改成任意 颜色。 我们可以一次改一个配色器的属性: PALETTE 属性,颜色代码(以后简称为颜色)。 但速度太慢,也可一次把所有配色器都改了: PALETTE USING 颜色数组 颜色数组通常是一个LONG型的数组,可以有下标,也可以省略。 比如PALETTE USING A&(13)表示从A&(13)开始n个变量表示配色器, n就是发色数。 也可以用PALETTE USING A&表示从A&(0)开始的 n个配色器。 配色器的颜色代码是这样: 模式0和9都是64最大发色数,用个整数就能表示。它是按二进制数表示的: R'G'B'R G B 共六位,RGB就是红绿蓝了,加个撇代表亮度高点的RGB。 二进制100100就代表最亮的红色(亮红+红=特亮的红),转成十进制 就是32+4=36,在立即窗里输入PALETTE 0,36就能看到..... 用不带参数的PALETTE可以恢复原来默认的配色器设置。 默认是 0黑 1深蓝 2深绿 3青 4红 5紫红 6棕 7浅灰 8深灰 9蓝 10绿 11浅青 12亮红 13亮紫 14黄 15亮白 发现了么?是按二进制IRGB的规律排列的,I是亮度位。 模式12和模式13是64的三次方种发色数,用三个字节表示,只能用 LONG型变量。计算方法是: C&=G * &H10000 + B * &H100 + R 注意,RGB的顺序颠倒过来了,是BGR。 R,G,B分别是红绿蓝三个分量的值,范围是0 - 63 这样的颜色数远超过高彩色(65536)了,可惜不能同屏显示出来。 但要显示一张模糊的照片还是可以的。 :) Q>C&=G * &H10000 + B * &H100 + R Q>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Q>这是什么东西,看不明白. ;( A>十六进制是两个数表示一个字节,比如FF就是255,是一个字节. A>而FFFF是两个字节,是65535. 这在一些游戏攻略上经常能见到. A>G是绿色,B是兰色,R是红色,各占一个字节,表示一种颜色.要把它们 A>合在一起,就把第二个字节乘以&h100,第三个乘&h10000,这跟十进制 A>乘法很象的. 关于模式12,在第一部分就讲过了,请读者回去看看,试试能不能 用配色器把那个图形弄得更奇怪一些。 :) 模式13是最有用的模式,因为它的配色器最多,同一屏幕显示的颜色 曾经是最多的,所占显存又远小于模式12,因此适用于此模式的GIF格式 图形曾经特别流行,直到现在真彩色模式的应用才使JPG普及开。 看下面这个程序: --- =============== SCREEN 13 DIM pal(511) AS LONG FOR i = 0 TO 255 STEP 2 LINE (0, i)-(639, i * 2), i / 2, BF NEXT FOR i = 0 TO 511 pal(i) = (i MOD 64) * &H10000 NEXT j = 0 WHILE INKEY$ = "" PALETTE USING pal(j) j = (j + 1) MOD 256 WEND --- =============== 运行时你会发现其速度之慢不可忍受,这是QB的一大BUG,于是我们用下面 这段代码替换PALETTE那行,速度就快多了: --- =============== FOR i = 0 TO 255 OUT &H3C8, i + j OUT &H3C9, pal(i) AND &HFF OUT &H3C9, (pal(i) AND &HFF00) / &H100 OUT &H3C9, (pal(i) AND &HFF0000) / &H10000 NEXT --- =============== 计算机的各种设备都连在设备总线上,都有一些端口用于操作。 OUT是对计算机的端口进行操作的命令。前面是端口号,后面的是数据。 端口&h3C8和&h3C9是DAC寄存器,&H3c8是索引,指出要操作哪个寄存器, &h3C9是数据寄存器,第一次是B,第二次G,第三次R。如果用INP函数 可以读出DAC寄存器的值,比如: OUT &h3C8,1 ?"BLUE:";INP(&H3C9) ?"GREEN:";INP(&H3C9) ?"RED:";INP(&H3C9) 可显示属性1的各分量。 上面那段程序仍然不是很快,没办法。以后我们讲混合编程时也许 可以用汇编语言混合编程来解决。事实上,如果要用QB做比较快速 的动画,通常要汇编语言的帮助,但你不并需要学习汇编,有现成的 函数可用。 注意:由于端口号不同,模式9不能使用上面的方法,因为配色器少, 使用PALETTE足够了。 Q>我怎么知道哪个端口是干什么的呢,DAC是什么东西. Q>这点有点乱. A>这就有点复杂了,要查资料,否则我也不知道. A>可以买本硬件的书,但你不懂汇编语言估计看不懂. A>怎么给你写的你就照抄就完了,不必管它是怎么实现的,把 A>那一大堆语句当一句PALETTE就成了. :) 说了这么半天,还没讲怎么画图呢。别着急,还要先说一下坐标系统: QB默认以屏幕左上角为0,0点,右下角为最大位置,由分辨率决定, 比如对于分辨率是640X480的模式12就是 (639,479)。 这跟习惯上的笛卡尔坐标系不一样,Y轴方向反了。 如果画图时超过这个限制会自动截断。QB本身提供了一些坐标变换 和放缩的命令转换坐标。看下例: --- ================= SCREEN 12 'WINDOW SCREEN (-10, 0)-(100, 100) 'VIEW (0, 10)-(100, 100) LINE (110, 120)-(130, 140), 3, B LINE (40, 40)-(60, 60), 3, BF CIRCLE (0, 0), 40, 10 PAINT (0, 0), 14, 10 --- ================= 输入后先运行一遍,再把WINDOW前面那个撇去掉试试,再把VIEW前面 哪个撇去掉试试,最后把WINDOW后面那个SCREEN去掉试试 还有可以去掉前面WIDTH那个撇号看看效果。 WINDOW命令是调整屏幕坐标系统用的。如果后面没有跟着SCREEN则会 把后面坐标中小的当成左下角,跟笛卡尔坐标的习惯相同,否则 还跟以前一样,小的是左上角。WINDOW能把显示区域移到物理屏幕外, 并且把逻辑坐标放缩成物理坐标。具体的你可以改改上面那个例子看看。 我的书上是用图来表示的,我没办法给大家画出来。 :( VIEW命令是设置可见区域,刚才说了,QB把超过屏幕范围的图形自动删掉, 而VIEW可以把超过指定范围的图形删掉,好象在屏幕里做个小屏幕。 VIEW的语法定义是: VIEW(x1,y1)-(x2,y2),颜色,边框颜色 颜色是在VIEW框内的颜色,边框颜色是框外的颜色。 提醒一下,所谓颜色其实是属性,并非真正的颜色,要看配色器。 没有任何参数的VIEW和WINDOW就能恢复原坐标系统,但画上的图是改不了了。 把WINDOW语句虚拟的坐标系叫做逻辑坐标,系统本身的是物理坐标,用PMAP函 数可以转换逻辑坐标和物理坐标,看HELP吧。 ;) 我想通过上面这个例子,你可以理解怎样用LINE画实心和空心方块了。 画圆是CIRCLE,也可以画弧和椭圆,方法是: CIRCLE (X,Y),半径,颜色,起始角度,结束角度,纵横比 两个角度都是用弧度表示的,纵横比是画椭圆时用的,可以自己试试看。 由于模式9和13的屏幕纵横比不是4:3,画出来的圆可能是椭圆,如果不指定, QB会自动调整纵横比,但是画出来的方块可就变成长方形了,需要自己算, 或者用WINDOW命令调整。 Q>想起一个问题,画扇面的程序我在学校编的时侯 Q>不太正常,斜线不是正好连接了两个角,有点歪.是不 Q>是学校显示器的问题? A>这就是QB画圆命令的"纵横比"的问题,由于你用的显示模式的每个象素不是 A>1:1的,是长的,圆的纵横坐标也不相同. A>简单的解决的办法是用SCREEN 12. PAINT是填充图形,PAINT(X,Y),填充色,边框色 如果省略边框色默认是和填充色一样的。QB的填充方法跟PHOTOSHOP可不一样, 它是寻找边框色而不是寻找不同颜色.如果没遇到边框色,会把整个屏幕都充 满,有时遇到一些图形却不能充满,比如两个圆组成的环,你可以试试看。 PAINT还可以填充花纹图形。我觉得实际用途也不大,而且要了解显示屏幕页 面的知识,在M$的手册里讲了4大篇,我就懒得说了。 在QB的范例里有个EDPAT.BAS文件,是示范如何使用花纹填充的。 无论是QB还是MSBASIC,都附带许多示范,虽然做的挺漂亮,但不容易看懂。 你可以运行试试看。 下面是我高中时在GWBASIC上编的一个程序,去了行号,是关于花纹填充的。 --- ================================ DATA 255,75,75,123,255,149,149,221,255,106,106 DATA 239,255,253,253,255,255,38,38,231,255,25 DATA 25,127,255,100,100,124,-1 SCREEN 9 PALETTE 0, 1 w: READ a IF a = -1 THEN GOTO E: a$ = CHR$(a) + a$ i = i + 1 GOTO w: E: PAINT (12, 12), a$ --- ================================ 由于GWBASIC不是结构化语言,上面这个程序编的不好,用了GOTO语句, 其实完全可以用WHILE代替,读者自己改成WHILE吧。 READ命令是读取DATA语句存放的数据。如果有大量数据一般放到单独的 文件里,但速度较慢。如果少量数据,不如就用DATA命令。 DATA命令后各数据间用逗号分隔,写多少行都可以,QB认为他们都是连 在一起的。DATA可以放到程序中任何位置,QB自动跳过。 DATA命令的存放方式是字符串,即使编译成EXE文件后也能用PCTOOLS等 “看见”文本状态的数据。因此不能用引号,因为引号代表字符串,但 没有引号也代表字符串。由READ命令判断输入数据是数字还是字符串。 如果光输逗号则被认为是空字符串。 READ语句是顺序读取数据的,读到最后如果没有数据则会出错。 READ语句可以同时读取不同类型的几个变量。 可以用RESTORE语句恢复起始位置,从头开始。RESTORE命令 后也可加行号,表示恢复到某一行。比如: data1: DATA 2,2 data2: DATA &HFF,3,a,4,5,b ... ... RESTORE data2 READ a,b,c$ ? a,b,c$ 输出结果: 255 3 a 其中前两个是数字,最后一个是字符串。 上面我们解决了二维变换的问题,如果程序需要放大缩小旋转等操作 一般还是用数学方法来解决,这里就不多讨论了。 下面我们讨论三维图形: --- =================== SCREEN 12 xu = 350 yu = 80 zu = -30 z = 10 'Z的初值 WIDTH ,30 '你可以把这里换成60试试。模式12的文本模式有80X30和80X60两种 '注意逗号不可少,因为第一个参数是列,第二个才是行. COLOR 13 LOCATE 27,1 PRINT "3D test" COLOR 9 DO p = -zu / (z - zu) FOR x = 0 TO 1000 STEP 2 '步长值的大小决定画出来的速度和质量,改改看 y = 10 * SIN(.05 * SQR(x * x + z * z)) x1 = xu + (xu - x) * p y1 = yu + (xu - y) * p PSET (x1, y1) NEXT z = z + 5 LOOP WHILE INKEY$ = "" --- =================== 三围....咳....三维变换公式是: p = -zu / (z - zu) x1 = xu + (xu - x) * p y1 = yu + (xu - y) * p 其中x,y,z是要显示象素的三维坐标,xu,yu,zu是视角(就是你的眼睛)的坐标, 因为把Z轴方向指向屏幕里,所以你的眼睛的Z坐标是负的。X,Y坐标的指向跟 以前一样。而x1,y1就是投影变换以后的屏幕坐标了。 原理我不说了,通过这个公式可以把空间上一点投影到屏幕上,让眼睛感觉上是 立体的。 y = 10 * SIN(.05 * SQR(x * x + z * z))是一个三维曲线函数,就是你所 看到的那个波浪图。 PSET是画点,后面可以加颜色,比如 PSET(10,10),10 但我省略了,QB按前面COLOR命令设的默认颜色画。实际上所有绘图 命令都可以省略颜色。 在图形模式下打印字跟文本模式一样只是文本分辨率不同。模式9 还是80X25;模式12有两种,一个是80x30一个是80x60,可以用WIDTH 设置;模式13只有40X25,而且返回模式0后仍然保持40X25,比如要用 WIDTH 80改回80X25。打印的颜色取决于COLOR命令 与PSET对应的还有一个PRESET语句,是用来擦点,我觉得没什么用。 我们以前已经见到过COLOR命令了,是在文本模式下。 在模式9里背景颜色是整个屏幕的背景,只要一改整个全改,而不是个别字符。 在模式12和13里根本不能用背景颜色这项,COLOR命令只能有一个参数 就是前景色。 因此如果在上面写字的话文字的背景色一定是黑色的,除非用PALETTE改配色器。 绝对坐标和相对坐标:以前所用的都是绝对坐标,是相对(0,0)的坐标, QB也可用相对坐标,在每个坐标前加STEP就成了,这时括号里的坐标 是在上一次绘图的坐标基础上移动的数值。 还有个绘图语句是DRAW,它是一种小型的语言,用字符串表示, 有点象LOGO语言。 看下面这段程序: SCREEN 12 DRAW "bm200,100" '把绘图点移动到200,100,前缀b代表不画 DRAW "c1;s50;" 'c后跟的是颜色,S后是比例,可以改改比例看看。 FOR r = -360 TO 360 STEP 144 DRAW "TA" + STR$(r) DRAW "r10" SLEEP NEXT SLEEP是按任意键继续的命令,后面可以加个数字,表示等待多少秒后 继续,如果不加就只能永远期待你按键盘了。 关于DRAW的绘图命令请自己看HELP吧,我认为没什么用,很少用过。 比如下面这段程序是我刚翻出来的,是高中时编着玩的: CONST pi = 3.1415926535# SCREEN 12 VIEW (1, 1)-(280, 191), , 1 VIEW PAINT (319, 199), 1, 1 a = 20 - 180 l = 90 x = 90 y = 55 PSET (x, y) FOR i = 1 TO 9 SLEEP a = a - 20 + 180 y = y - l * SIN(a * pi / 180) x = x + l * COS(a * pi / 180) LINE -(x, y) NEXT LOCATE 20: PRINT "Copyright (c) W.H.C": PRINT "corporation" SLEEP 通过这个九角星的程序能进一步理解VIEW和LINE语句的用法。 LINE语句是可以省略第一个坐标参数的,这时就以上次画的最后 一点为起点开始画。 绘图语句刚说完就费了这么大篇幅,动画和屏幕页都没讲呢。 :( 今天又太累了,不写了。以后我可能还要再写关于鼠标, EMS内存切换,声卡编程等等比较高级的QB程序编程方法,也许 写VB初步入门,不知道写哪方面的好。 留个"作业"吧,前面有钟表的例子,请用绘图命令编个漂亮点的钟表。 下篇预告:用QB也能做出三维动画?真的!而且动感十足, 没有闪烁!
这部是最精彩的,你将可以编3D动画了! 补充点,上次忘了说那个3D图形的公式中SQR函数是开平方,与x^.5的 作用是相同的,但速度比后者快. 而X*X这种用法也是为了提高速度而不用X^2 上次我们讨论了QB的基本绘图命令,然而用那些命令要做动画是不成的. 我们知道,动画的原理就是一幅幅反复显示出来,在每一时刻都只有一帧 图象. 最简单的动画要属擦图动画了,看下面这段例子: --- ===================================== SCREEN 12 CONST r = 10 COLOR 14 LINE (0, 200 + r)-(640, 200 + r), 10 FOR i = 0 TO 320 STEP .5 x = i * 2 y = 200 - ABS(SIN(i * .2) * 200) 'ABS取绝对值 CIRCLE (x, y), r PAINT (x, y) t1! = TIMER '延时开始 WHILE TIMER < t1! + .01 WEND '延时结束 LINE (x - r, y - r)-(x + r, y + r), 0, BF IF INKEY$ = CHR$(27) THEN END NEXT --- ===================================== 不好意思,我用取绝对值的正弦函数代替自由落体的函数. 似乎是动起来了,效果也不错.但因为这是一个简单的图形,画的时间 比较短,现在的CPU时间比较快. 如果你把延时部分放到LINE的后面 或者去掉你就知道问题所在了. 如果是复杂的图形,当画的过程所费时间与延时时间相近时就会发生闪烁. 事实上这个程序也有闪烁,只是速度比较快,眼睛反应不过来. 你可以试试把半径R改大一些. QB提供了GET和PUT语句来提高绘图的速度. 下面修改一下上面的程序: --- ===================================== SCREEN 12 CONST r = 20 COLOR 14 bpp = 1 Planes = 4 l = 4 + INT(((r * 2 + 1) * (bpp) + 7) / 8) * Planes * (r * 2 + 1) l = l / 2 + 1 DIM cir(l) AS INTEGER CIRCLE (200, 200), r PAINT (200, 200) GET (200 - r, 200 - r)-(200 + r, 200 + r), cir(0) CLS '清屏语句 LINE (0, 300 + r)-(640, 300 + r), 10 FOR i = 20 TO 300 STEP .5 x = i * 2 y = 300 - ABS(SIN(i * .2) * 200) PUT (x - 20, y - 20), cir(0) t1! = TIMER WHILE TIMER < t1! + .01 WEND LINE (x - r, y - r)-(x + r, y + r), 0, BF IF INKEY$ = CHR$(27) THEN END NEXT --- ===================================== 我们把复杂的图形事先画好,用GET语句把点阵数据保存在一个数组变量里, 然后在动画循环中用PUT命令一次画出,效果好一些. 但数组变量的大小应该是多少呢?多了浪费,少了要出错误,QB的HELP里有这样 一个公式: l = 4 + INT(((X2-X1+ 1) * (bpp) + 7) / 8) * Planes * (Y2-Y1 + 1) X1,X2,Y1,Y2是图形块的左上和右下角坐标,bpp和planes与屏幕模式有关: Screen Mode bpP Planes 9 1 4 (if > 64K of EGA memory) 12 1 4 13 8 1 计算出的L是字节长.而在QB里的数字变量没有单字节的,最短也是2字节 的INTEGER.因此我们用L/2+1来表示INT数组长度. 其实也可以用其他类型的数组的. 需要注意的是,与其他语句不同,所有坐标必须都在屏幕的物理范围之内, 否则将会出错. PUT语句默认是把GET语句记录的图象与要覆盖的背景进行XOR运算. 我们再复习一下各种逻辑运算: NOT 取非,就是NOT 1=0,NOT 0=1 这里的1是二进制位,不是数字,数字应是-1,因为二进制1111...等于-1 AND 与运算,如果x AND y,只有两个数字都是1才等于1,否则为0 OR 或运算,只要有一个是1,结果就是1 XOR 异或,当两边不相等为1,否则为0. XOR有一种非常有趣的性质,就是任意交换性,比如A XOR B=C, 则A XOR C=B,C XOR B=A....任意两个数交换位置都可以. 而且只要不是0,通常运算结果跟运算前不同. 这种性质给加密等操作带来很大的方便. PUT语句后面可以加上各种符号,表示原图与背景进行的运算方式. 前面说QB模式是XOR方式,还有PSET方式(直接覆盖),PRESET方式 (取非后覆盖),OR方式(与背景取或),AND方式(与背景取与) 看看下面这段程序,我们把前面的程序加上背景,事实上大多数动画都是要有 背景的. --- ===================================== SCREEN 12 CONST r = 20 COLOR 14 bpp = 1 Planes = 4 l = 4 + INT(((r * 2 + 1) * (bpp) + 7) / 8) * Planes * (r * 2 + 1) l = l / 2 + 1 DIM cir(l) AS INTEGER CIRCLE (200, 200), r PAINT (200, 200) GET (200 - r, 200 - r)-(200 + r, 200 + r), cir CLS FOR i = 0 TO 640 STEP 10 '画背景 LINE (i, 0)-(i, 479), 3 NEXT LINE (0, 300 + r)-(640, 300 + r), 10 FOR i = 20 TO 300 STEP .5 x = i * 2 y = 300 - ABS(SIN(i * .2) * 200) PUT (x - 20, y - 20), cir, XOR '试把这里改成其他符号,如PSET等. t1! = TIMER WHILE TIMER < t1! + .01 WEND PUT (x - 20, y - 20), cir '默认是XOR,可以在保留背景的同时擦图 'LINE (x - r, y - r)-(x + r, y + r), 0, BF '原来的方法把背景也给擦 了 IF INKEY$ = CHR$(27) THEN END NEXT --- ===================================== 我们发现,XOR的缺点是只要不是黑色就会变色,跟以前不一样了. 因此要做得好一点应该先把要被覆盖的背景用个数组存下来, 当下一帧时再把那个"备份"覆盖回去,缺点是闪烁感很强. 其实擦图动画永远解决不了闪烁的问题的,因为屏幕上总有一段时间 那个图形消失了,一亮一灭,这就造成了闪烁,无论速度多快,只要 屏幕上显示了不该显示的东西,人眼就能反映出来. 真正的解决办法是用双缓冲技术.这种技术把要画在屏幕上的东西事先 在内存中画出来,然后把整个页全部覆盖. 然而QB对内存的控制很严, 给你操作内存的语句很少也很慢,根本不能完成这种技术,而且所有 绘图语句也都不能用,要用位运算来完成. 其实象JAVA语言一样,这些限制的好处是安全性提高了,但JAVA本身就提供 了双缓冲. :( 替代的办法是屏幕页技术. 显示卡从硬件上来说,显示内存足够存放 很多页.可由于编QB时内存还很贵,通常的显示卡只支持模式9的双 屏幕页,这也是为什么我要讲模式9的原因. 模式9从颜色属性来说跟模式12一样,但发色数和分辨率要低得多. 由于分辨率是640X350,象素是长的点而不是方点,如果你画个正方形 就会发现变长方形了,这需要注意. 但CIRCLE会自动调整纵横比,画出来 总是圆的. 屏幕页的控制用SCREEN语句的第三,四个参数: SCREEN ,,绘图页,显示页 注意前面那两个逗号少不得.默认两个页都是0. 画图时在绘图页上画,在屏幕上显示不出来,屏幕上显示的是显示页, 两页无关,这样用户就不会看见绘图过程.当绘图页画好后你当然可以 用SCREEN语句把绘图页和显示页交换,但实际上交换时可能会发生闪烁, 这是硬件的原因.因此我们用PCOPY语句把一页拷贝到另一页去,速度很快. 再看下面这个例子: --- ===================================== SCREEN 9 CONST r = 40 '加大半径,如果你把以前的例子也加大半径就会发生严重的闪烁 COLOR 14 bpp = 1 Planes = 4 l = 4 + INT(((r * 2 + 1) * (bpp) + 7) / 8) * Planes * (r * 2 + 1) l = l / 2 + 1 DIM cir(l) AS INTEGER, backup(l) AS INTEGER CIRCLE (200, 200), r PAINT (200, 200) GET (200 - r, 200 - r)-(200 + r, 200 + r), cir CLS FOR i = 0 TO 640 STEP 10 LINE (i, 0)-(i, 479), 3 NEXT LINE (0, 300 + r)-(640, 300 + r), 10 PCOPY 0, 1 SCREEN , , 0, 1 ' 显示第0页绘图,第1页显示 FOR i = 20 TO 300 STEP .5 x = i * 2 y = 300 - ABS(SIN(i * .2) * 200) GET (x - r, y - r)-(x + r, y + r), backup '先把要覆盖的背景备份 PUT (x - r, y - r), cir '你可以用任何方法贴图. PCOPY 0, 1 t1! = TIMER WHILE TIMER < t1! + .01 WEND PUT (x - r, y - r), backup, PSET '恢复背景 'LINE (x - r, y - r)-(x + r, y + r), 0, BF IF INKEY$ = CHR$(27) THEN END NEXT --- ===================================== 不错吧?一点闪烁都没有. 但QB做动画还是不成,因为没有透明色方式. 在动画中动的物体通常被称 为"精灵",精灵的背景通常是黑色,但如果背景不是黑色,用PSET就会很难看, 如果用XOR精灵的"身体"就会变色.这时就需要一种"通明色",贴上去只显示 身体,不显示背景. 很不幸的,DOS下的程序都很难完成这种操作,WIN下恐怕 也要DIRECTX技术的帮助,我原来曾想用汇编语言编函数,可后来一想,都WIN95 时代了,DOS就凑合用吧. 动画技术基本讲完了,一般的书上还有"字符动画",我觉得意义不大, 字符模式可以有4到8页,但不能用PCOPY,闪烁好象更厉害. :P 可以参考我的BATTOOL3.2版,我想很多站的DOS工具区都有这个软件. 里面有用字符动画做的一只小猪的动画,还是用批处理做的,改成QB 程序太容易了. 但是绘图技术不仅这些,它需要很多的数学知识. 比如我那本最早的BASIC 书里讲述了各种用矩阵变换计算各种二维三维图形,各种三维图形的做法等. <电脑爱好者>几年前也连载过用QB做动画的教程,似乎是能做出来三国志的 片头动画(但效果可是....嘿嘿,能看出来是人就不错了) 希望在实际应用时多运用数学工具,先想出数学模型,在转换成QB语句. 下面我们要讲模块化编程了. 主要有两个语句:SUB 和 FUNCTION SUB的作用是自己定义一个过程,以后在QB里可以象调用语句那样调用它, 或者说就是"自定义语句"或是"子程序". FUNCTION的作用就是"自定义函数",可以象调用函数那样调用FUNCTION过程. 使用过程除了简化一段反复使用的程序外,它可以使一个复杂的工程问题 简单化,先解决"主要矛盾",把一些难解决的细节问题放到过程里. 在过程的内部的变量和其他过程,主程序间是没有关系的. 也就是说你可以在过程外边用for i=....循环,循环里面有个过程, 过程里面也有个for i=...循环,但这两个变量i是没有关系的. 这就是过程的优点,当程序很大的时