编程百科全书

在这里,相信会找到令你尖叫的文章!

导航

Small Basic初体验

究竟是IDE太神奇还是大神太神奇呢?

——吐槽

几天前跟vczh大(萌)神上Q,发现了微软推出了一个针对小童的高级玩具,为Small Basic设计的一个界面很很很“清爽”的IDE。

介绍:http://news.cnblogs.com/n/90535/

英文教程:http://msdn.microsoft.com/en-us/beginner/ff384126.aspx

中文教程:http://wenku.baidu.com/view/ae6cb3b665ce050876321313.html?1298180940

笔者是个贪新鲜又超级好奇的人,于是下载了来体验了一下。6MB不够的安装文件,一份绝对简明易懂、图文并茂(萌点众多)的手册,绝对是(宅)程序员居家旅行、杀人越货的必备。

一、灰常易用

经体验,此IDE果然非常Kids,非常简明好用。在一边与大(萌)神聊Q一边写着玩的情况下,从未接触过Basic的笔者就凭借瞎猜和IDE的提示写出了一个很弱智的猜数字游戏~

想当年学C++啊,对着个黑框,写个猜数字啊~(哎,不说了……)

当然并不是说C/C++不好,笔者虽然贪新,但并非是忘旧之徒(对MM还是很……目不斜视的啦)。关于Small Basic跟C/C++(哎呀,都是笔者的亲爹娘啊)的对比,一会儿再谈。

结合google,搜索了一下该语言产生随机数的一个方法,再加上IDE和伟大的瞎猜,到这里,一个很简单的猜数字游戏就完成了。

没接触过Basic的笔者,甚至不用参考教程,至今花费了15分钟。

结论:此IDE非常易用!!!

二、不会传递参数的函数机制 vs 递归画分形

 一点吐槽:Small Basic虽然语法结构很简单,直观,也支持用户自定义子例程(也就是函数)这样的机制。但是在Small Basic里面,作用域这个概念对于用户的数据和自定义子例程(当然Small Basic本身有提供带参数的方法,不在此文讨论范围)来说似乎是浮云。

在Small Basic中,一切用户定义的变量都是全局范围。而且变量的声明和定义顺序并不会影响程序的编译运行。

比起C/C++对作用域以及变量声明定义顺序的严格要求,Small Basic似乎宽松很多。

个人认为,这样对于一些小型程序(毕竟这个IDE就是让孩子闹着玩)来说是很好的一个设计。毕竟在闹着玩的时候也不想顾虑那么多玩以外的因素,直接用代码表达自己的思想和做些花花绿绿的东西出来,不是很过瘾吗?

不过,宽松的同时也给代码本身造成了一定的局限。例如不接受参数传递的子例程机制,在需要实现一些函数的递归调用时就比较悲催。

以下,是笔者在vczh大(萌)神深度影响下写出来的一段关于生成 分形 的代码

  1'由于不支持自定义带参数的子例程,需要构造有参数的函数时
  2'唯有模拟一下参数传递的时候,堆栈中发生的事情
  3Sub PushStack
  4  JJStack[ index ] = parameter
  5  index = index + 1
  6EndSub
  7
  8Sub PopStack
  9  index = index - 1
 10  parameter = JJStack[ index ]
 11EndSub
 12
 13'如果是C/C++,应该能直接写出这样的函数,也只有在这些时候
 14'才体会到亲爹亲娘的好啊!!!
 15'void DrawTriangle( x1, y1, x2, y2, x3, y3, n )
 16Sub DrawTrianglePushRealParameters
 17  '把实参压入栈中,根据参数列表的顺序,从右到左入栈
 18  '这样是为了从栈中取参数的时候可以从左往右取
 19  parameter = n
 20  PushStack()
 21  parameter = y3
 22  PushStack()
 23  parameter = x3
 24  PushStack()
 25  parameter = y2
 26  PushStack()
 27  parameter = x2
 28  PushStack()
 29  parameter = y1
 30  PushStack()
 31  parameter = x1
 32  PushStack()
 33EndSub
 34
 35Sub DrawTriangleGetParameters
 36  'get parameters from the stack from top to botton
 37  '从栈中取出参数,复制给形式参数 v_xi
 38  v_x1 = JJStack[ index - 1 ]
 39  v_y1 = JJStack[ index - 2 ]
 40  v_x2 = JJStack[ index - 3 ]
 41  v_y2 = JJStack[ index - 4 ]
 42  v_x3 = JJStack[ index - 5 ]
 43  v_y3 = JJStack[ index - 6 ]
 44  v_n = JJStack[ index - 7 ]
 45EndSub
 46
 47'code sector of the function DrawTriangle
 48'相当于一个函数真正的代码段
 49'在vczh大神的深度影响下,笔者也把参数和代码分开来处理了
 50Sub DrawTriangleCode
 51  'print debug info
 52  'Sub PrintStack
 53    'For i = 0 To index - 1 
 54      'TextWindow.WriteLine( JJStack[ i ] )
 55    'EndFor
 56    'TextWindow.WriteLine( " " )
 57  'EndSub
 58  CalMidPoints()
 59  GraphicsWindow.DrawTriangle( x11, y11, x22, y22, x33, y33 )
 60  If v_n = 1 Then
 61    GraphicsWindow.DrawTriangle( x11, y11, x22, y22, x33, y33 )
 62  Else
 63    x1 = v_x1
 64    y1 = v_y1
 65    x2 = (v_x1 + v_x2) / 2
 66    y2 = (v_y1 + v_y2) / 2
 67    x3 = (v_x1 + v_x3) / 2
 68    y3 = (v_y1 + v_y3) / 2
 69    n = v_n - 1
 70    DrawTriangle()
 71    
 72    x1 = (v_x1 + v_x2) / 2
 73    y1 = (v_y1 + v_y2) / 2
 74    x2 = v_x2
 75    y2 = v_y2
 76    x3 = (v_x2 + v_x3) / 2
 77    y3 = v_y2
 78    n = v_n - 1
 79    DrawTriangle()
 80    
 81    x1 = (v_x1 + v_x3) / 2
 82    y1 = (v_y1 + v_y3) / 2
 83    x2 = (v_x2 + v_x3) / 2
 84    y2 = v_y2
 85    x3 = v_x3
 86    y3 = v_y3
 87    n = v_n - 1    
 88    DrawTriangle()
 89  EndIf
 90EndSub
 91
 92'函数调用结束之后,当然要清理栈中的数据咯
 93Sub DrawTriangleEnd
 94  For i = 1 to 7
 95    PopStack()
 96  EndFor
 97EndSub
 98
 99'大家一定有留意之前的DrawTriangleCode函数中无端端出现了这个函数
100'这里是为了表明,Small Basice赤裸裸地无视了声明顺序
101Sub CalMidPoints
102  x11 = (v_x2 + v_x3) / 2
103  y11 = v_y2
104  x22 = (v_x1 + v_x2) / 2 
105  y22 = (v_y1 + v_y2) / 2
106  x33 = (v_x1 + v_x3) / 2
107  y33 = (v_y1 + v_y3) / 2
108EndSub
109
110'干一些初始化工作,让窗口好看点,个人喜好咯,呵呵
111'当然还有很重要的一点:切勿在变量初始化之前就使用它们,不然……
112Sub  Init
113  GraphicsWindow.Title = "JJTriangle"
114  GraphicsWindow.Width = 600
115  GraphicsWindow.Height = 800
116  GraphicsWindow.BackgroundColor = "Black"
117  GraphicsWindow.DrawBoundText( 10, 10, 500, "JJ draw triangle" )
118  GraphicsWindow.PenColor = "Green"
119  GraphicsWindow.PenWidth = 1
120  
121  x1 = 300
122  y1 = 100
123  dely = 450
124  delx = dely / 1.732
125  x2 = x1 - delx
126  y2 = y1 + dely
127  x3 = x1 + delx
128  y3 = y1 + dely
129  
130  n = 6
131  index = 0
132  
133  GraphicsWindow.DrawTriangle( x1, y1, x2, y2, x3, y3 )
134EndSub
135
136'这里把之前的几个子例程写到一个子例程里,在所谓的main中直接调用就OK
137Sub DrawTriangle
138  DrawTrianglePushRealParameters()
139  DrawTriangleGetParameters()
140  DrawTriangleCode()
141  DrawTriangleEnd()
142  '关键,在调用完之后,把v_x1等还原,给下一个函数调用
143  '不然的话,由于Small Basic的变量是全局的,在第一次递归调用中子例程修改了
144  '自己的形参,其实也把父例程的形参也修改了,那就悲催了……
145  DrawTriangleGetParameters()
146EndSub
147
148'print debug info
149Sub PrintStack
150  For i = 0 To index - 1 
151    TextWindow.WriteLine( JJStack[ i ] )
152  EndFor
153  TextWindow.WriteLine( " " )
154EndSub
155
156
157'main
158Init()
159DrawTriangle()
160
161
162
163  



运行出来的效果也很美艳,如图:


做完之后,感觉很好玩。
模拟了一下函数调用时栈的活动情况,
为每一个函数维护了一下它自己的数据,
递归调用了函数自身。

用C定义有参数的函数的时候,总是觉得十分理所当然。但是用“懒惰”的Basic做了一下,才更加清楚了C或者C++在这些“理所当然”的操作上帮程序员处理了什么。

一直有很多人认为C++太复杂太庞大,但我总感觉这都是相对而言。有时用C/C++的时候会想起Basic的好,但用Basic的时候又难免会怀念起C字头的家伙来。

语言各有千秋,就像各种各样的MM一样……

不过有时用一种语言试图去实现别的语言的一些特性,还是挺好玩的。


三、用turtle画出各种各样的图形

微软真的提供了一个名副其实的“玩具”!

龟标是什么,笔者就不在这里详述了。
作为这篇文章的一个小节,代码不是重要的,关键是发现,原来有时候随心所欲玩玩也有很有趣的结果。

受够了设计算法和数据结构的复杂流程了吗?
那就用Move和Turn让白色的纸张丰富起来吧~







抱着玩的心态写程序,就会觉得像孩童一样快乐。
可能这就是Small Basic想带给用户的吧!


四、用Small Basic做一个能旋转的立方体

开始大(萌)神跟笔者说你可以试试用Small Basic做一个能根据鼠标动作旋转的立方体时,笔者真的有些不敢相信。

笔者没搞过图形学,但总觉得这个命题有点像图形学。
说起三维物体旋转,总会想起一大堆数学公式和一轮复杂的推导和数据处理。

光想就觉得很头大!

但最后还是做出来了……





用到的数学知识也比较基础,矩阵相乘和图形学基础里面的空间坐标旋转变换。

思路:
1)建立坐标系:眼睛到屏幕的距离、屏幕大小、物体到屏幕的距离
2)建立确定一个立方体需要的八个顶点,然后适合的点之间连起来就是一个立方体
3)根据相关数学知识,每次鼠标动作发生时更新点的坐标信息
4)按照投影比例把位置更新后的点投射到屏幕上

注意:
1)眼睛到屏幕的距离 跟 屏幕大小 的关系要注意好
,不然效果很诡异(笔者开始没注意到这一点,后来经vczh大(萌)神一点拨,本来诡异的图形立刻从爱丽丝仙境回到了现实世界)
2)真实世界的坐标到屏幕坐标的映射。屏幕坐标的y轴正方向与现实世界相反
3)遇到数学问题,除个别NB人士,切勿自行推导!!!

3D投影的比例计算:http://www.bitscn.com/school/Flash/200609/58746.html
3D旋转坐标的计算方法:http://www.docin.com/p-93975213.html

  1'http://www.docin.com/p-93975213.html
  2'http://www.bitscn.com/school/Flash/200609/58746.html
  3
  4'响应两个鼠标事件,参见手册
  5GraphicsWindow.MouseMove = OnMouseMove
  6GraphicsWindow.MouseDown = OnMouseDown
  7
  8'对窗口和坐标原点,几个距离的一些初始化
  9Sub Init
 10  GraphicsWindow.Title = "MouseMove"
 11  GraphicsWindow.BackgroundColor = "Black"
 12  GraphicsWindow.Width = 800
 13  GraphicsWindow.Height = 600
 14  offsetX = GraphicsWindow.Width / 2
 15  offsetY = GraphicsWindow.Height / 2
 16  dEyeToScreen = 500'眼睛到屏幕的距离,值的设定取决于设置的虚拟的屏幕大小
 17  dObjToScreen = 0
 18
 19  ShowCube()'显示立方体,具体定义在后面
 20EndSub
 21
 22'根据相关的数学知识,根据点到屏幕的距离(景深)确定投影比例系数
 23Sub GetScale
 24  Scale = dEyeToScreen / (dEyeToScreen + dObjToScreen)
 25EndSub
 26
 27Sub SetPoints
 28  sideLen = 200
 29  nPoints = 8
 30  len = 100
 31
 32  'point1
 33  x[ 0 ] = -1 * len
 34  y[ 0 ] = -1 * len
 35  z[ 0 ] = len
 36  'point2
 37  x[ 1 ] = -1 * len
 38  y[ 1 ] = len
 39  z[ 1 ] = len
 40  'point3
 41  x[ 2 ] = len
 42  y[ 2 ] = len
 43  z[ 2 ] = len
 44  'point4
 45  x[ 3 ] = len
 46  y[ 3 ] = -1 * len
 47  z[ 3 ] = len
 48  'point5
 49  x[ 4 ] = -1 * len
 50  y[ 4 ] = -1 * len
 51  z[ 4 ] = -1 * len
 52  'point6
 53  x[ 5 ] = -1 * len
 54  y[ 5 ] = len
 55  z[ 5 ] = -1 * len
 56  'point7
 57  x[ 6 ] = len
 58  y[ 6 ] = len
 59  z[ 6 ] = -1 * len
 60  'point8
 61  x[ 7 ] = len
 62  y[ 7 ] = -1 * len
 63  z[ 7 ] = -1 * len
 64
 65EndSub
 66
 67'x【 i】等是点的真实坐标,而ScreenX【 i】等则是点在屏幕上经过投影得到的坐标
 68Sub GetScreenPoints
 69  For i = 0 To 7
 70    dObjToScreen = z[ i ]
 71    GetScale()
 72    ScreenX[ i ] = x[ i ] * Scale + offsetX
 73    ScreenY[ i ] = y[ i ] * Scale + offsetY
 74  EndFor
 75EndSub
 76
 77Sub ShowScreenPoints 'for debugging only
 78  GraphicsWindow.BrushColor = "Green"
 79  For i = 0 To 7
 80    GraphicsWindow.FillEllipse( ScreenX[ i ] - 3, ScreenY[ i ] - 3, 6, 6 )
 81  EndFor
 82EndSub
 83
 84'构造立方体,把相关的顶点连接起来
 85Sub MakeCube
 86  GraphicsWindow.PenColor = "Red"
 87  GraphicsWindow.PenWidth = 1
 88  GraphicsWindow.DrawLine( ScreenX[ 0 ], ScreenY[ 0 ], ScreenX[ 1 ], ScreenY[ 1 ] )
 89  GraphicsWindow.DrawLine( ScreenX[ 0 ], ScreenY[ 0 ], ScreenX[ 3 ], ScreenY[ 3 ] )
 90  GraphicsWindow.DrawLine( ScreenX[ 0 ], ScreenY[ 0 ], ScreenX[ 4 ], ScreenY[ 4 ] )
 91  GraphicsWindow.DrawLine( ScreenX[ 2 ], ScreenY[ 2 ], ScreenX[ 1 ], ScreenY[ 1 ] )
 92  GraphicsWindow.DrawLine( ScreenX[ 2 ], ScreenY[ 2 ], ScreenX[ 3 ], ScreenY[ 3 ] )
 93  GraphicsWindow.DrawLine( ScreenX[ 2 ], ScreenY[ 2 ], ScreenX[ 6 ], ScreenY[ 6 ] )
 94  GraphicsWindow.DrawLine( ScreenX[ 7 ], ScreenY[ 7 ], ScreenX[ 3 ], ScreenY[ 3 ] )
 95  GraphicsWindow.DrawLine( ScreenX[ 7 ], ScreenY[ 7 ], ScreenX[ 4 ], ScreenY[ 4 ] )
 96  GraphicsWindow.DrawLine( ScreenX[ 7 ], ScreenY[ 7 ], ScreenX[ 6 ], ScreenY[ 6 ] )
 97  GraphicsWindow.DrawLine( ScreenX[ 5 ], ScreenY[ 5 ], ScreenX[ 1 ], ScreenY[ 1 ] )
 98  GraphicsWindow.DrawLine( ScreenX[ 5 ], ScreenY[ 5 ], ScreenX[ 4 ], ScreenY[ 4 ] )
 99  GraphicsWindow.DrawLine( ScreenX[ 5 ], ScreenY[ 5 ], ScreenX[ 6 ], ScreenY[ 6 ] )
100EndSub
101
102Sub ShowCube
103  SetPoints()
104  GetScreenPoints()
105  MakeCube()
106EndSub  
107
108'不是真的“删除”立方体,而是在立方体旋转变换的过程中,消除上一个位置的立方体的残影
109Sub DeleteCube
110  GraphicsWindow.Clear()
111  GraphicsWindow.BackgroundColor = "Black"
112  GraphicsWindow.BrushColor = "Red"
113  GraphicsWindow.FillEllipse( offsetX - 10, offsetY - 10, 20, 20 )
114EndSub
115
116'每次鼠标位置更新都会调用的旋转函数   
117Sub Rotation
118  'debug code
119  'PrintScreenX()
120  angle = anglex
121  SetRotationMatrix()
122  '由于Small Basic用户自定义变量的全局性,这里只好另外设置数组p和pp作为RotateX等
123  '函数的“形参”
124  For i = 0 to 7
125    p[ "x" ] = x[ i ]
126    p[ "y" ] = y[ i ]
127    p[ "z" ] = z[ i ]
128    RotateY()'这个意思是绕y轴旋转
129    dObjToScreen = pp[ "z" ]
130    x[ i ] = -1 * pp[ "x" ] '由于真实世界跟屏幕的坐标不是一致的,因此要反过来
131    y[ i ] = pp[ "y" ]
132    z[ i ] = pp[ "z" ]
133    GetScale()
134    'ScreenX[ i ] = x[ i ] * Scale + offsetX
135    'ScreenY[ i ] = y[ i ] * Scale + offsetY
136    ScreenX[ i ] = pp[ "x" ] * Scale + offsetX
137    ScreenY[ i ] = pp[ "y" ] * Scale + offsetY
138  Endfor
139  
140  angle = angley
141  SetRotationMatrix()
142  For i = 0 to 7
143    p[ "x" ] = x[ i ]
144    p[ "y" ] = y[ i ]
145    p[ "z" ] = z[ i ]
146    RotateX()
147    dObjToScreen = pp[ "z" ]
148    x[ i ] = -1 * pp[ "x" ]
149    y[ i ] = pp[ "y" ]
150    z[ i ] = pp[ "z" ]
151    GetScale()
152    'ScreenX[ i ] = x[ i ] * Scale + offsetX
153    'ScreenY[ i ] = y[ i ] * Scale + offsetY
154    ScreenX[ i ] = pp[ "x" ] * Scale + offsetX
155    ScreenY[ i ] = pp[ "y" ] * Scale + offsetY
156  Endfor
157  'ShowScreenPoints()
158EndSub
159
160'响应鼠标消息,左键按下开始旋转,右键按下清屏
161Sub OnMouseDown
162  If Mouse.IsRightButtonDown Then
163    GraphicsWindow.Clear()
164    bBegin = 0
165  Else 
166    If Mouse.IsLeftButtonDown Then
167      Init()
168      GraphicsWindow.BrushColor = "Red"
169      GraphicsWindow.FillEllipse( offsetX - 10, offsetY - 10, 20, 20 )
170      bBegin = 1
171      originX = GraphicsWindow.MouseX
172      originY = GraphicsWindow.MouseY
173    EndIf
174  EndIf
175EndSub
176
177'鼠标移动就旋转屏幕中的立方体
178Sub OnMouseMove
179  If bBegin = 1 Then
180    DeleteCube()
181    '用鼠标移动的坐标差来判断鼠标移动方向以及旋转方向。 除以100是为了减慢旋转速度
182    anglex = (GraphicsWindow.MouseX - originX) / 100
183    angley = (GraphicsWindow.MouseY - originY) / 100
184    Rotation()
185    MakeCube()
186    '记录下当前的鼠标坐标,作为下次鼠标位置更新时的“前”鼠标位置坐标使用
187    originX = GraphicsWindow.MouseX
188    originY = GraphicsWindow.MouseY
189  EndIf
190EndSub
191
192'关于矩阵的部分,其实就是一些乘法和坐标变换,数学推导和证明在开头的两个URL已经有提及
193''''''''''''''''''''''Matrix Part'''''''''''''''''''''''''''''
194Sub SetRotationMatrix
195  RotateMatrix[ 0 ][ 0 ] = Math.Cos( angle )
196  RotateMatrix[ 0 ][ 1 ] = -1 * Math.Sin( angle )
197  RotateMatrix[ 1 ][ 0 ] = Math.Sin( angle )
198  RotateMatrix[ 1 ][ 1 ] = Math.Cos( angle )
199EndSub
200
201
202p[ "x" ] = 0
203p[ "y" ] = 0
204p[ "z" ] = 0
205
206pp[ "x" ] = 0
207pp[ "y" ] = 0
208pp[ "z" ] = 0
209
210'void MatrixNul( _in par[ 0 ], _in par[ 1 ],  _out rpar1, _out rpar2 )
211par[ 0 ] = 0
212par[ 1 ] = 0
213Sub MatrixMul
214  rPar1 = 0
215  rPar2 = 0
216  For ii = 0 to 1 
217    rpar1 = rPar1 + RotateMatrix[ 0 ][ ii ] * par[ ii ]
218  EndFor
219  For ii = 0 to 1
220    rPar2 = rPar2 + RotateMatrix[ 1 ][ ii ] * par[ ii ]
221  Endfor
222EndSub
223
224'void Rotate( _in p[ x, y, z ], _out pp[ x, y, z ] )
225'rotate center is (0, 0, 0)
226Sub RotateZ
227  pp[ "z" ] = p[ "z" ]
228  par[ 0 ] = p[ "x" ]
229  par[ 1 ] = p[ "y" ]
230  MatrixMul()
231  pp[ "x" ] = rPar1
232  pp[ "y" ] = rPar2
233EndSub
234
235Sub RotateY
236  pp[ "y" ] = p[ "y" ]
237  par[ 0 ] = p[ "z" ]
238  par[ 1 ] = p[ "x" ]
239  MatrixMul()
240  pp[ "z" ] = rPar1
241  pp[ "x" ] = rPar2
242EndSub
243
244Sub RotateX
245  pp[ "x" ] = p[ "x" ]
246  par[ 0 ] = p[ "y" ]
247  par[ 1 ] = p[ "z" ]
248  MatrixMul()
249  pp[ "y" ] = rPar1
250  pp[ "z" ] = rPar2
251EndSub
252


做完了,一点小感想:
1)Small Basic依然好玩,但是不会减少我对C字头家族的爱。不过以后又多了一种工具,哈哈!
2)热爱数学是好的,但是不自量力强行推导数学结论会浪费时间。所以作为一个程序员,对于又爱又恨的数学,还是大胆滴抄吧!(大(萌)神都这么说)抄了之后,神马都是浮云!
3)这个Small Basic的IDE没有调试功能哦,笔者是故意留在最后说的,大家耐心滴打印调试信息吧!!!哈哈哈哈!
4)Small Basic初体验,Good!

posted on 2011-02-24 22:45  天天编程  阅读(4378)  评论(1编辑  收藏  举报