编程语言实用化指南——写在最后

在本书中,我们一起制作了crowbar和Diksam两种编程语言。让我感到欣慰的是,书中的示例程序不只是停留在入门级别,而是达到了实用语言的水准。

亲爱的读者朋友们,希望你们也能尝试制作属于自己的编程语言。不过说出来你们可能会大跌眼镜, 编程语言的魅力基本上是由它的程序库来决定的 ,而这是不容争辩的事实。

例如,Perl由于正则表达式等强有力的字符串处理功能得到了广泛的应用。在Perl4的时候,作为编程语言,Perl既没有引用,也不能创建数据结构,可以说很难用。但是,因为它处理文本文件十分方便,所以得到了广泛使用。同样, PHP也因为提供了很多面向网页应用的功能而得到了普及。语言是否实用,是否能够普及,实际上和语言的设计本身没有太大关系。

因此,我在发明了crowbar和Diksam两种语言后,为它们加载了各自的程序库。

首先,我为crowbar加载了鬼车,使它具有用正则表达式处理文本的能力。

在Diksam中我用crowbar来处理文本。在文本处理这个领域里已经有了Perl、Ruby等语言,因此就算是为此特地制作一门语言也不会得到广泛普及。用来开发Web应用的编程语言更是琳琅满目,比如PHP、Perl、Ruby、Java、ASP、ASP.NET等,在这个领域中还充斥着各种框架,可以说是一个大杂烩。租赁服务器更是让人头疼,好不容易做的网页应用,有可能会因为服务器不能支持而不能使用。就这点来说,已经很让人沮丧了。

因此,我考虑让Diksam定位为“让初学者可以制作简单游戏的语言”。

在很早之前,我自己就是这样走上了编程的道路。

那个时候(1980年左右)的个人电脑,大多将BASIC作为标准配置。那个时候的编程语言没有 IF 语句中 begin~end 或者 {} 之类的程序块的概念,选择分支的时候必须使用 GOTO行号的方式进行跳转。也没有循环结构的 FOR语句。要在循环外面记录循环计数器后,再使用 GOTO 进行跳转。虽然可以使用 GOSUB制作子程序,但是不能定义局部变量,所有变量都要当做全局变量来处理。此外,变量名字不看到第2个字符,是区分不出来的 [1]。当然,这是时代的选择,不过,当时的BASIC作为编程语言来说还真是不怎么样。

即使如此,我当时只用了几十行代码就可以写一款射击游戏(用字符“┴”当做炮台来击落用字符“-o-”做成的飞碟)。我觉得这个过程十分有趣,这也是我踏上编程学习道路的第一步。

但是对于现在的年轻人来说,却不知道怎么去实现一款简单的游戏。

现在这些PC的性能与当时相比可以说有了飞跃性的提高,也出现了各种各样的编程语言和免费的处理器。但是,比如在C语言中,使用不依赖处理器的标准C,就连窗体都打不开。即便只是在Windows中能够运行起来的程序,C语言也要通过Windows的API来创建窗口,如此复杂的程序初学者根本应付不来。

我觉得在现在C语言的入门书中,多半从“ hello,world.”开始介绍许多命令行程序。但是,我在中学时代,从最开始就能写出和“ hello,world.”差不多的程序,接着就编了猜数字游戏,然后就想着要做一款打飞碟的游戏了。当今,计算机已经十分先进,但人们在这个方面反而退化了。

当然,现在不仅可以使用C语言,也考虑使用Java。Java中的GUI可以不依赖于处理器,因此也可以把游戏做成Applet发布在网站上,在朋友面前炫耀一把。但这样一来,在创建类的时候就需要继承一种叫作 java.applet. Applet的类,并重写它的 init()和 paint()之类的方法。这里突然出现了很多面向对象的知识,初学者一时之间很难接受。也许有人会觉得我又在这里老调重弹了,但是仔细想想,为了从GUI接收输入,就必须要创建事件监听器、实现特定的接口,除此之外还需要使用内部类和匿名类。这些还没完,因为制作的是实时游戏,为了实现动画效果还需要使用多线程进行异步处理。这些对于一个新手来说简直是个噩梦。

再者,制作“打飞碟”这样一款游戏对于JavaScript来说也不是很容易。可以制作FLASH的语言ActionScript,它的标准处理器又不是免费的。

HSP(Hot Soup Processor)语言是我在中学时代玩过的类似于BASIC的语言。不过,很对不起这门语言的fans,这门语言本身和与它同时代的BASIC如出一辙,因此对于刚开始学习编程的人来说非常不推荐。它甚至没有GOSUB [2]

因此,我在Diksam中加载了可以让“打飞碟”游戏实现起来更为简单的程序库 [3]

因为要制作的游戏非常简单,所以无须特意想着面向对象和事件驱动的概念。例如“打飞碟”游戏可以写成下面这样。

代码清单1-4 “打飞碟”游戏的程序(ufo.dkm)

1: require diksam.window;

2:

3: // 创建设置窗体属性的WindowAttribute对象。

4: WindowAttribute attr = create_window_attribute();

5: // 设置背景色为黑色。

6: attr.background = create_solid_brush(0, 0, 0);

7:

8: // 创建窗体。如果使用默认设置就可以,

9: // 那么不创建attr传入null即可。

10: Window w = create_window(“UFO 游戏”, 800, 600, attr);

11:

12: // 使用x键终止程序。

13: w.set_destroy_proc(window_destroy_and_exit);

14: // 显示窗体。

15: w.show();

16:

17: // 为了描绘窗体取得Graphics接口。

18: Graphic g = w.get_graphics();

19: // 将字符的背景色设置为黑色。

20: g.set_background_color(new Color(0, 0, 0));

21:

22: // 战车、激光、UFO的颜色设置。

23: Color tank_color = new Color(60, 255, 100);

24: Color ufo_color = new Color(60, 255, 255);

25: Color beam_color = new Color(255, 255, 100);

26: // 生成字体。详细的设置(FontAttribute)

27: // 与WindowAttribute相同,当前默认为null。

28: Font font = create_font(25, null);

29:

30: // 随机数的初始化

31: randomize();

32:

33: // 游戏结束后再开始使用的循环

34: for(;;){

35: // 设定炮台(tank)、ufo的坐标。将tank_x,ufo_x,ufo_y的

36: // 当前坐标赋值给prev,作为前一次绘制的坐标(消除时使用)。

37: // ufo_next_x,y作为ufo的移动目标的坐标。

38: // ufo将在ufo_next_x,y的附近移动,

39: // 但如果两次坐标基本相同,则重新设定ufo_next_x,y。

40: int tank_x = 0;

41: int ufo_x = 0;

42: int ufo_y = 0;

43: int ufo_prev_x = ufo_x;

44: int ufo_prev_y = ufo_y;

45: int ufo_next_x = random(700);

46: int ufo_next_y = random(450);

47: // 是否存在炮台发射的激光的标识和激光坐标

48: boolean beam_flag = false;

49: int beam_prev_y;

50: int beam_x;

51: int beam_y;

52:

53: // 游戏的主循环

54: for(;;){

55:  // 消除前一次画出来的UFO。

56:  g.draw_string(font, ufo_color, ufo_prev_x, ufp_prev_y," ");

57:  // 绘制UFO。

58:  g.draw_string(font, ufo_color, ufo_x, ufp_y," ├O┤ ");

59:  // 为了再次消除,保存本次绘制的坐标。

60:  ufo_prev_x = ufo_x;

61:  ufo_prev_y = ufo_y;

62:  // 绘制炮台。

63:  g.draw_string(font, ufo_color, tank_x, 540," /┴\ ");

64:  // 发射了激光的话……

65:  if(beam_flag){

66:   // 消除前面的激光,重画新的激光。

67:   g.draw_string(font, ufo_color, ufo_prev_x, ufp_prev_y," ");

68:   g.draw_string(font, ufo_color, ufo_x, ufp_y,"|");

69:   beam_prev_y = beam_y;

70:

71:   // 碰撞判断。可能很幼稚。

72:   if(beam_x >= ufo_x && beam_x < ufo_x + 80

73:    && beam_y >= ufo_y && beam_y < ufo_y +60){

74:    // 被激光打中后跳出循环。

75:    break;

76:   }

77:  }

78:

79:  // 通过判断键盘输入移动炮台。

80:  if(is_key_pressed(KeyCode.LEFT) && tank_x > 0){

81:   tank_x -= 10;

82:  } elseif(is_key_pressed(KeyCode.RIGHT) && tank_x < 700){

83:   tank_x += 10;

84:  }

85:  if(is_key_pressed(KeyCode.SPACE) && !beam_flag){

86:   beam_flag = true;

87:   beam_x = tank_x + 40;

88:   beam_y = beam_prev_y = 480;

89:  }

90:  // UFO的移动。在ufo_next_x,y的附近移动。

91:  if(ufo_x < ufo_next_x -10){

92:   ufo_x += 10;

93:  } elseif(ufo_x > ufo_next_x + 10){

94:   ufo_x -= 10;

95:  } else {

96:   // 如果两次坐标基本相同,则重新设定目标坐标。

97:   ufo_next_x = random(700);

98:  }

99:  if(ufo_y < ufo_next_y - 10) {

100:   ufo_y += 10;

101:  } elseif (ufo_y > ufo_next_y + 10) {

102:   ufo_y -= 10;

103:  } else {

104:   ufo_next_y = random(450);

105:  }

106:  // 激光的移动

107:  if(beam_flag) {

108:   if(beam_y < -20){

109:    beam_flag = false;

110:   }

111:   beam_y -= 20;

112:  }

113:  // 定时消息循环。无论有没有

114:  // 鼠标或者键盘事件,都等待20毫秒。

115:  timed_message_loop(w, 20);

116: }

117:

118: // 被激光打中后跳出循环,执行这里。

119: for(;;){

120:  // 显示爆炸效果

121:  Color explosion_color = new Color(255, 0, 0);

122:  g.draw_string(font, explosion_color, ufo_x, ufo_y, "*");

123:  timed_message_loop(w, 100);

124:  g.draw_string(font, explosion_color, ufo_x, ufo_y, "###");

125:  timed_message_loop(w, 100);

126:  // 按N重启游戏,按Q退出。

127:  if(is_key_pressed(KeyCode.N)){

128:   Brush b = create_solic_brush(0, 0, 0);

129:   g.fill_rectangle(b, 0, 0, 800, 600);

130:   b.dispose();

131:   break;

132:  } elsif (is_key_pressed(KeyCode.Q)) {

133:   exit(0);

134:  }

135: }

136: }

游戏的截屏如下所示。

figure_0392_0105

这是个跟我同时代的人都会怀念的画面吧 [4]

Diksam在这个领域的进化旅程才刚开始,谁也不知道它在未来会变成什么样子。但是,在代码清单“ufo.dkm”的程序中,只能有一架UFO,在同一时间炮台只能发射一发激光(因为表示UFO和激光位置的变量只有一组)。如果觉得这样没意思的话,就必须要使用数组了。x坐标和y坐标要是都使用数组来管理的话,肯定会很不方便,如果有类的话感觉就会方便很多。同样,即使不同时出现多个飞碟,如果想要各种各样的敌人轮番登场,就要使用继承和多态了……一门语言因为追寻着这样的思路而具有了面向对象的概念,真让人兴奋。

各位读者朋友,你们想让自己的编程语言向哪个方向发展呢?希望本书能给各位带来一些启发。

注 释

[1]. Visual Basic的名字虽然继承了Basic,但其实是完全不同的另一种语言。

[2]. 从HSP3开始可以通过函数实现。

[3]. 这种情况仅限于Windows平台。

[4]. 当然,在Diksam中也可以很简单地使用图标来表示UFO。