[English Version]

MFP语言和可编程科学计算器

MFP语言简介

MFP函数

部署用户自定义函数

在您的应用中调用MFP

创建安卓安装包

小游戏的开发

小游戏基础

处理图像和声音

绘制文字

一个贪吃蛇游戏

一个销宝石游戏

一个超级小白兔游戏

多设备多玩家游戏

绘制图形

使用MFP进行数学分析

使用MFP处理文件

数,字符串和数组

日期时间和系统相关

可编程科学计算器介绍

MFP编程语言帮助:处理声音和图像

在前面的章节中已经介绍了通过MFP编程的方式实现动画的原理。但是,上述编程方式存在一个问题。由于每一个物体,无论是墙,按钮还是蛇都需要多次调用绘图函数进行绘制,绘制一个比较复杂的图案,需要生成大量的绘图事件,如果直接绘制到屏幕上,频繁读写速度较慢的显存将占用大量的计算时间。更好的解决办法是将所有的图案绘制到一个图像对象中,然后直接将该图像对象“贴”到显示窗口上。这样就只需要一个绘图事件来读写显存,效率大大提高。这就涉及到了图像处理的问题。

此外,在运行游戏时常常需要发出各种声音,如何播放声音文件也将在本节中涉及。

1. 创建,装载,克隆和保存图像

以下函数用于创建,装载,克隆和保存图像,以及获取图像的大小和判断内存中的图像句柄是否依然有效。

函数名

函数帮助信息

create_image

::mfp::multimedia::image_lib::create_image(2) :

create_image(w, h)返回一个全新的,空白的被包装过的JAVA图像对象。该图形对象的宽度为w,高度为h。

load_image

::mfp::multimedia::image_lib::load_image(1) :

load_image(image_path)返回一个被包装过的JAVA图像对象。它有一个参数image_path。这个参数是一个基于字符串的,指向一个图形文件的路径。

load_image_from_zip

::mfp::multimedia::image_lib::load_image_from_zip(3) :

load_image_from_zip(zip_file_name, zip_entry_path, zip_file_type)返回一个被包装过的JAVA图像对象。该图形对象从一个zip压缩文件中读取。它的第一个参数是基于字符串的zip文件的压缩路径。它的第二个参数是图像在该zip文件中的压缩路径。它的第三个参数要么是0,要么是1。如果等于0,表示普通的zip文件,而如果等于1,表示MFP App的安卓assets中的zip文件。

clone_image

::mfp::multimedia::image_lib::clone_image(7) :

clone_image(image_src, src_left, src_top, src_right, src_bottom, dest_width, dest_height)返回一个新的被包装过的JAVA图像对象。该图形对象的宽度为dest_width,高度为dest_height。这个被返回的图像是(被拉缩过的)原图像image_src的被选中的区块的拷贝。被选中的区块的左、上、右、下的坐标分别是src_left、src_top、src_right和src_bottom。注意src_left、src_top、src_right和src_botto是可选参数,它们的缺省值分别是0、0、image_src的宽度和image_src的高度。dest_width和dest_height也是可选参数。它们的缺省值分别是src_right - src_left以及src_bottom - src_top。本函数的一个例子为 clone_image(img_src, 0, 0, 100, 200, 50, 300)。

save_image

::mfp::multimedia::image_lib::save_image(3) :

save_image(image, file_format, path)保存一个被包装过的JAVA图像对象至一个图形文件。本函数的第一个参数是被包装过的JAVA图像对象,第二个参数是基于字符串的图像文件的格式,当前仅支持"png","jpg"以及"bmp"格式。第三个参数是图像文件的路径。如果成功保存,本函数返回True,否则返回False。本函数的一个例子为:save_image(img, "png", "C:\\Temp\\1.png")

get_image_size

::mfp::multimedia::image_lib::get_image_size(1) :

get_image_size(image_handle)返回由image_handle所代表的被包装过的JAVA图像对象的长和宽组成的数组。

is_valid_image_handle

::mfp::multimedia::image_lib::is_valid_image_handle(1) :

is_valid_image_handle(image_handle)返回一个布尔量,用于告诉开发人员一个被包装过的JAVA图像对象,也就是image_handle参数,是否依然合法还是已经被关闭。

2. 修改图像

在图像装入内存之后,在将其贴到屏幕上之前,开发者往往需要对它进行修改,也就是在图像对象上进一步作图。为了方便开发者,MFP提供了一套和在显示窗口作图完全一致的作图函数。用户需要调用open_image_display函数,为图像创建一个“显示窗口”(也就是image display)。该显示窗口的大小和图像的大小一致(和真正的游戏显示窗口类似,图像的“显示窗口”也可以由set_display_size函数进行调整),图像对象则变成图像的“显示窗口”的背景图片,然后开发者调用诸如draw_rect,draw_image等函数在“显示窗口”绘图,和真正的游戏显示窗口类似,开发者也可以调用update_display和drop_old_painting_requests函数对绘图事件和绘图时间进行控制。绘图完成后,需要调用get_display_snapshot函数(该函数也适用于真正的游戏显示窗口)获得“显示窗口”的截图,也就是修改后的图像对象,这个截取的图像对象就可以为draw_image函数所用,直接往游戏显示窗口上“贴”。最后开发者调用shutdown_display关闭“显示窗口”。

这里需要注意几点。首先,为图像创建显示窗口并作图不会改变原有图像,换句话说,做完图,shutdown_display之后,原有图像还是老样子。get_display_snapshot只是返回一个新的图像对象的句柄。第二,和真正的游戏显示窗口类似,如果生成太多的绘图事件,更新图像将会花费很多计算时间,一个好的办法是调用set_display_snapshot_as_bgrnd函数。这个函数将显示窗口(不论是图像的“显示窗口”,也就是image display,还是真正的游戏显示窗口,也就是screen display)的截屏作为显示窗口新的背景图像。这样原有的绘图事件就可以移除而不必重绘了。

上述函数的详细说明如下:

函数名

函数帮助信息

open_image_display

::mfp::multimedia::image_lib::open_image_display(1) :

open_image_display(image_path_or_handle)创建一个image display供开发人员调用MFP函数绘图。它有一个参数。这个参数既可以是一个基于字符串的,指向一个图形文件的路径,也可以是null,还可以是一个由load_image,load_image_from_zip,create_image或者clone_image函数返回的JAVA image对象的句柄。

get_display_snapshot

::mfp::graph_lib::display::get_display_snapshot(4) :

get_display_snapshot(display, update_screen_or_not, width_ratio, height_ratio)返回一个display(既可以是screen display,也可以是image display)的截屏。它的第二个参数,update_screen_or_not,告诉MFP在截屏之前该display是否需要刷新;它的第三个和第四个参数,是可选参数,缺省值均为1,分别用于告诉MFP返回的截屏的长度和高度的缩放比例。比如,get_display_snapshot(d, true, 0.5, 3)首先刷新屏幕d,然后截取d的图像,最后将截取的图像长度上压缩为原来的一半,高度上拉伸为原来的三倍并返回新的图像。

set_display_snapshot_as_bgrnd

::mfp::graph_lib::display::set_display_snapshot_as_bgrnd(3) :

set_display_snapshot_as_bgrnd(display, update_screen_or_not, clear_callbacks_or_not)将一个display(既可以是screen display,也可以是image display)的截屏设置为它的背景图案。它的第二个参数,update_screen_or_not,告诉MFP在截屏之前该display是否需要刷新;它的第三个参数,clear_callbacks_or_not,告诉MFP是否需要将屏幕绘图事件序列清空。比如,set_display_snapshot_as_bgrnd(d, true, true)首先刷新屏幕d,然后将屏幕绘图事件序列清空,最后截屏并将所得图像作为屏幕背景图像。

以下代码给出了贪吃蛇游戏中通过使用图像“显示窗口”(也就是image display)绘制该游戏的静止的物件的示例。图像“显示窗口”的截屏被贪吃蛇游戏拿来用作游戏的实际显示窗口(也就是screen display)的背景图案。这样有效避免了多步绘制各种小物件,加快了计算速度:

    // open an empty image display

    // 打开一个空的图像显示窗口(image display)

    variable boardImageDisplay = open_image_display(null)

    // adjust it's size to game display window's size times scaling ratio

    // 将图像显示窗口的大小调整为游戏真实显示窗口的大小乘以缩放系数

    set_display_size(boardImageDisplay, windowWidth * scalingRatio, windowHeight * scalingRatio)

    // calculate text origin of level information

    // 计算通关级数信息文字的起始位置

    variable textOrigin = [10, 10]

    variable levelFontSize = LEVELFONTSIZE()

    if xMargin > yMargin

        // text is in the center of left edge rectangle

        // 文字位于左边缘长方形的正中

        textOrigin = calculate_text_origin(DISPLAYSURF, "level " + level, [0, 0], xMargin, windowHeight, 0, 0, levelFontSize)

    else

        // text is in the center of top edge rectangle

        // 文字位于上边缘长方形的正中

        textOrigin = calculate_text_origin(DISPLAYSURF, "level " + level, [0, 0], windowWidth, yMargin, 0, 0, levelFontSize)

    endif

    // draw level information text. Note that the text is scaled down to fit the image.

    // 绘制通关级数信息文字。注意由于图像显示窗口的真实尺寸比游戏真实显示窗口的尺寸要小,文字被相应缩小了。

    draw_text("static element", boardImageDisplay, "level " + level, textOrigin * scalingRatio, scoreColor, levelFontSize * scalingRatio)

    // draw the border of snake's moving space. Note that the rectangle is scaled down to fit the image.

    // 绘制蛇的移动空间的边界。注意由于图像显示窗口的真实尺寸比游戏真实显示窗口的尺寸要小,边界矩形被相应缩小了。

    draw_rect("static element", boardImageDisplay, [xMargin, yMargin] * scalingRatio, gridWidthDim * scaledCellSize, gridHeightDim * scaledCellSize, boarderColor, 1)  

    // draw the wall. Note that the wall is scaled down to fit the image.

    // 绘制墙体。注意由于图像显示窗口的真实尺寸比游戏真实显示窗口的尺寸要小,墙体被相应缩小了。

    drawPoints("static element", boardImageDisplay, wallPlace, wallColor, cellSize, xMargin, yMargin, scalingRatio)

    if(shouldDrawButtons)

        // we draw text only for each button because button text is static while button border is not.

        // 在这里仅仅绘制按钮上的文字而不绘制按钮的边框因为文字不会变化,而边框会随着按下弹起发生变化。

        drawButtonText(boardImageDisplay, upBtnLT, btnW, btnH, "Up", false, scalingRatio)

        drawButtonText(boardImageDisplay, downBtnLT, btnW, btnH, "Down", false, scalingRatio)

        drawButtonText(boardImageDisplay, leftBtnLT, btnW, btnH, "Left", false, scalingRatio)

        drawButtonText(boardImageDisplay, rightBtnLT, btnW, btnH, "Right", false, scalingRatio)

    endif

    // get snapshot of the image display, note that we update the image display before taking snapshot

    // 取回图像显示窗口的截图。注意在获取截图前,先将图像显示窗口更新。

    variable boardImage = get_display_snapshot(boardImageDisplay, true)

    // shutdown image display

    // 关闭图像显示窗口

    shutdown_display(boardImageDisplay)

    // set the snapshot of the image display to be game's display window's background image.

    // note that the mode is stretching the background image to fit the whole game's display window

    // as the snapshot image is smaller than the game's display window.

    // 将上述图像显示窗口的截屏设置为游戏真实显示窗口的背景图案。注意背景图案的设置模式是1,也就是缩放背景图案让它和

    // 游戏真实显示窗口大小一致。

    set_display_bgrnd_image(DISPLAYSURF, boardImage, 1)

3. 绘制图像

为显示窗口的绘图事件调度器添加一个绘制图像的函数是draw_image。该函数有两种不同的调用方法:第一个是draw_image(owner_info, display, image_or_path, left, top, width_ratio, height_ratio, painting_extra_info)。第二个是draw_image(owner_info, display, image_or_path, srcx1, srcy1, srcx2, srcy2, destx1, desty1, destx2, desty2, painting_extra_info)。在这两种不同的调用方式中,第一个参数是owner_info。Owner_info告诉绘图事件调度器谁拥有这个绘图事件。Owner_info可以是一个字符串,代表拥有者的名字,也可以是一个整数,代表拥有者的id,还可以是NULL,代表系统拥有该事件,更可以是一个包含两个元素的数组,其中第一个元素是一个代表拥有者名字的字符串,或者代表拥有者id的整数,或者代表系统的NULL,第二个元素是一个代表时标的浮点数,但要注意这里的时标不是真正的时标,该浮点数可以是任意值。该浮点数的值在清除本绘图事件时会发挥作用。第二个参数是display,也就是显示屏幕的句柄。第三个参数是图像的句柄或者是一个指向图像文件的地址字符串。最后一个参数是painting_extra_info,它告诉绘图事件调度器采用什么样的porterduff模式来绘制目标图像。这个参数是可选参数。porterduff模式内部机制比较复杂,建议开发者省略这个参数(也就是使用参数的缺省值)。如果开发者想要详细了解painting extra info,可以参考set_porterduff_mode以及get_porterduff_mode的函数帮助信息。如果开发者想要详细了解porterduff模式,建议阅读相关的JAVA文档。在第一种调用方式中,从第四个到第七个参数分别是图像将被绘制的位置的左边界的坐标,图像将被绘制的位置的上边界坐标,图像绘制时沿长度方向的缩放比例(是一个可以省略的参数,它的缺省值为1),以及图像绘制时沿高度方向的缩放比例(是一个可以省略的参数,它的缺省值为1)。在第二种调用方式中,从第四个到第十一个参数分别是选取图像将被绘制的部分的源长方形的左边界,选取图像将被绘制的部分的源长方形的上边界,选取图像将被绘制的部分的源长方形的右边界,选取图像将被绘制的部分的源长方形的下边界,目标位置的左边界,目标位置的上边界,目标位置的右边界以及目标位置的下边界。Draw_image的例子包括:draw_image("image", display, get_upper_level_path(get_src_file_path()) + "gem4.png", 48, 157) ,draw_image("image", display, gem3Img, 148, 257, 3, 0.5)以及draw_image("imagesrc", display, gem3Img, 0, 0, 32, 32, 210, 540, 300, 580, a_painting_extra_info)。

4. 声音的播放

MFP为开发者提供了一系列的声音处理函数可以实现声音的播放,循环播放,音量调整和停止。这些声音处理函数如下:

函数名

函数帮助信息

play_sound

::mfp::multimedia::audio_lib::play_sound(4) :

play_sound(source_path, repeat_or_not, volume, create_new_or_not)演奏一个声音文件,该声音文件可以是wave文件,也可以是midi文件,还可以是mp3文件。该函数返回一个演奏器的句柄,该句柄指向一个JAVA或安卓的多媒体演奏器。由于多媒体演奏器的资源是有限的,本函数会尽可能的回收并重用以前生成的多媒体演奏器。本函数有4个参数。第一个参数是声音文件的路径。第二个参数是一个布尔值,表示该声音是否需要重复演奏,这是一个缺省参数,缺省值是false。第三个参数是一个从0到1的浮点数,表示音量大小。这也是一个缺省参数,缺省值是1。第四个参数是一个布尔值,表示是否无论如何都强制生产一个新的多媒体演奏器。这也是一个缺省参数,缺省值是false。

play_sound_from_zip

::mfp::multimedia::audio_lib::play_sound_from_zip(6) :

play_sound_from_zip(source_zip_file_path, zip_entry_path, zip_file_type, repeat_or_not, volume, create_new_or_not)演奏一个从zip文件中抽取出的声音文件,该声音文件可以是wave文件,也可以是midi文件,还可以是mp3文件。该函数返回一个演奏器的句柄,该句柄指向一个JAVA或安卓的多媒体演奏器。由于多媒体演奏器的资源是有限的,本函数会尽可能的回收并重用以前生成的多媒体演奏器。本函数有6个参数。第一个参数是zip文件的路径。第二个参数是被压缩的声音文件的在zip文件中的位置路径。第三个参数是一个布尔值,0表示zip文件是普通的压缩文件,1表示zip文件位于MFP app的安卓asset目录中。第四个参数是一个布尔值,表示该声音是否需要重复演奏,这是一个缺省参数,缺省值是false。第五个参数是一个从0到1的浮点数,表示音量大小。这也是一个缺省参数,缺省值是1。第六个参数是一个布尔值,表示是否无论如何都强制生产一个新的多媒体演奏器。这也是一个缺省参数,缺省值是false。

start_sound

::mfp::multimedia::audio_lib::start_sound(1) :

start_sound(sound_handle)演奏sound_handle所指向的声音文件。如果该声音文件已经启动,这个函数什么也不做。

stop_all_sounds

::mfp::multimedia::audio_lib::stop_all_sounds(0) :

stop_all_sounds()停止所有正在播放的声音。

stop_sound

::mfp::multimedia::audio_lib::stop_sound(1) :

stop_sound(sound_handle)停止sound_handle所代表的声音的播放。如果该声音没有播放,这个函数什么也不做。

get_sound_path

::mfp::multimedia::audio_lib::get_sound_path(1) :

get_sound_path(sound_handle)返回sound_handle所指向的声音文件的路径。

get_sound_reference_path

::mfp::multimedia::audio_lib::get_sound_reference_path(1) :

get_sound_reference_path(sound_handle)返回sound_handle所指向的声音引用文件的路径。如果声音文件不是从zip压缩的读入的,声音引用文件和sound_handle所指向的声音文件(也就是get_sound_file函数的返回值)是同一个文件。如果声音文件是从zip压缩的读入的,声音引用文件路径是压缩文件的路径加上声音文件的压缩路径,比如"/folder1/folder2/snd.zip/zipped_folder/snd.wav",这里"/folder1/folder2/snd.zip"是压缩文件路径,"zipped_folder/snd.wav"是声音文件的压缩路径。

get_sound_repeat

::mfp::multimedia::audio_lib::get_sound_repeat(1) :

get_sound_repeat(sound_handle)返回一个布尔量,表示参数sound_handle所代表的声音是否会被重复演奏。

get_sound_source_type

::mfp::multimedia::audio_lib::get_sound_source_type(1) :

get_sound_source_type(sound_handle)返回一个整数,代表sound_handle所指向的声音引用文件的类型。0表示常规文件,1表示压缩的zip文件,2表示引用于MFP App中安卓asset中的zip文件。

get_sound_volume

::mfp::multimedia::audio_lib::get_sound_volume(1) :

get_sound_volume(sound_handle)返回参数sound_handle所代表的声音的音量(一个变化范围从0到1的浮点数)。

set_sound_repeat

::mfp::multimedia::audio_lib::set_sound_repeat(2) :

set_sound_repeat(sound_handle, repeat_or_not)设置一个sound_handle所代表的声音是否重复演奏。

set_sound_volume

::mfp::multimedia::audio_lib::set_sound_volume(2) :

set_sound_volume(sound_handle, volume)设置一个sound_handle所代表的声音的音量,注意音量参数volume的值变化范围是从0到1。

在这里需要注意的是,由于资源有限,MFP会尽可能重复使用声音演奏器。MFP判断一个演奏器是否可以重复利用的标准是比较声音文件的引用路径(reference path),如果该声音文件的引用路径和一个已有的演奏器的引用路径完全一样,MFP会认为已有的演奏器可以重复利用。否则MFP会生成一个新的演奏器资源。

声音文件的引用路径在大多数情况下和声音文件的实际路径一样。但如果声音文件是从一个zip文件,包括MFP应用的asset下的zip文件,中读出,该声音文件会被拷贝到SD卡或者硬盘上的一个临时的位置。那么该声音引用文件路径是压缩文件的路径加上声音文件的压缩路径,比如"/folder1/folder2/snd.zip/zipped_folder/snd.wav",这里"/folder1/folder2/snd.zip"是压缩文件路径,"zipped_folder/snd.wav"是声音文件的压缩路径。而该声音文件的路径则是拷贝到临时位置的路径。在这种情况下,引用路径和路径是不一样的。