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大(萌)神深度影响下写出来的一段关于生成 分形 的代码
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
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!