• 易迪拓培训,专注于微波、射频、天线设计工程师的培养
首页 > 测试测量 > 技术文章 > LabWindows/CVI虚拟仪器游戏设计之: 下雪场景

LabWindows/CVI虚拟仪器游戏设计之: 下雪场景

录入:edatop.com    点击:

4.3 下雪场景

4.3.1 下雪场景设计方法

动画场景(背景)在动画以及游戏设计中起到烘托角色和渲染气氛的作用。动画场景分为场景和背景两部分,场景是角色可以穿行其中的活动场面或自然景观;背景是起烘托和渲染作用的角色背后的景色。场景是动画设计过程当中不可缺少的一部分,在某种意义上,场景可以决定整个动画及游戏的风格。场景设计所涉及的面很广,要找到场景与整体风格的切入点,达到情节的和谐统一。

动画场景的创意设计与其他创意设计有相似的过程和方法。在创意前要做周密的设计和准备,即使创意不同凡响,也要使创意循着一个确定目标去发展。动画场景的创意设计是整个动画创意的一部分,应以整体效果为最终目标来思考和把握。实际上,动画场景设计的所有创意要建立在理解动画情境的基础上,动画设计师应不断与程序设计者在整体构思上保持一致,保证创意的完整性和协调性。

从时间上讲,场景艺术也就是时间艺术,在心理空间中营造物理空间的虚拟造型和概念,根据动画及游戏来调节场景的大小、高度等。而动画场景的运动性是建立在时间和空间的基础上的,空间中的角色和物体由于速度的变化而引起的位移和变形,设计者可以充分发挥想象的空间,制造出高水平的视觉效果。

在LabWindows/CVI 程序开发中,场景制作无疑是一个非常重要的环节,对于一些注重细节的用户来说,美观的界面,动态的效果无疑能大大提高软件的亲和力。对于下雪场景大家都不会陌生,用程序代码来控制雪花的下落与堆积也是比较有意思的,其原理并不复杂。先将背景设置为黑色,随机画出许多白点(雪花),然后使这些雪花不断地向下移动,并在底部形成堆积效果,随时间的流失逐渐融化,循环往复,一个下雪场景就完成了。

4.3.2 下雪场景程序设计

(1)面板设计

编写一个下雪场景效果程序,背景设置为黑色,随机产生白色的雪花,随时间的流失不断下落,堆积到地上,雪层不断加厚,但雪层也会随之部分融化,产生一种比较逼真的效果。背景采用Canvas 控件,雪花由CanvasDrawPoint 函数实现,雪花下落由异步定时器控制。面板设计如图4-7 所示,面板中主要控件属性设置如表4-5 所示。

edatop.com

图4-7 下雪场景面板

表4-5 控件属性设置表

常量名

控件类型

控件的主要属性

PANEL

Panel

标题:下雪场景回调函数:PanelCB

CANVAS

 Canvas

( 下雪场景背景)

(2)程序源代码

//头文件声明 
#include "toolbox.h"  
#include "asynctmr.h"  
#include   
#include   
#include "下雪场景.h"  
//全局静态变量 
static int TimerID;  
static int panelHandle;  
static int Snow[1000][3];  
static int PWidth;  
static int PHeight;  
//声明异步定时器 
int CVICALLBACK timer (int reserved, int timerId, int event, void *callbackData, int eventData1, int eventData2);  
//主函数 
int main (int argc, char *argv[])  
{  
int i;  
if (InitCVIRTE (0, argv, 0) == 0)  
return –1;  
if ((panelHandle = LoadPanel (0, " 下雪场景.uir", PANEL)) < 0)  
return –1; 
SetCtrlAttribute (panelHandle, PANEL_CANVAS, ATTR_PEN_COLOR, VAL_WHITE); 
SetCtrlAttribute (panelHandle, PANEL_CANVAS, ATTR_PEN_WIDTH, 1); 
GetPanelAttribute (panelHandle, ATTR_HEIGHT, &PHeight); 
GetPanelAttribute (panelHandle, ATTR_WIDTH, &PWidth); 
//用当前时间产生随机数 
SetRandomSeed (0); 
//随机产生雪花 
for (i = 0; i < 1000; i ++)  
{  
Snow[i][0] = (int) (Random(0, 1) * PWidth);  
Snow[i][1] = (int) (Random(0, 1) * PHeight);  
Snow[i][2] = 10 + (Random(0, 1) * 5);  
}  
//装载异步定时器 
TimerID = NewAsyncTimer (0.2, –1, 1, timer, 0); 
DisplayPanel (panelHandle);  
RunUserInterface ();  
//释放异步定时器资源 
DiscardAsyncTimer (TimerID);  
DiscardPanel (panelHandle);  
return 0; 
}  
//面板回调函数 
int CVICALLBACK PanelCB (int panel, int event, void *callbackData,  
int eventData1, int eventData2)  
{  
switch (event)  
{  
case EVENT_CLOSE:  
// 停止异步定时器 
SuspendAsyncTimerCallbacks (); 
// 延时0.2s  
Delay (0.2);  
QuitUserInterface (0);  
break; 
}  
return 0;  
}  
//异步定时器——绘制下雪场景 
int CVICALLBACK timer (int reserved, int timerId, int event, void *callbackData, int eventData1, int eventData2)  
{  
int i;  
static int OldX;  
static int OldY;  
static int colory;  
static int RGBValue;  
switch (event)  
{  
case EVENT_TIMER_TICK: 
// 禁止弹出运行时错误对话框 
DisableBreakOnLibraryErrors ();  
for (i = 0; i < 1000; i ++)  

OldX = Snow[i][0];  
OldY = Snow[i][1];  
Snow[i][1] = Snow[i][1] + Snow[i][2];  
if (Snow[i][1] > PHeight) 

Snow[i][1] = 0;  
Snow[i][2] = 5 + ((double)rand () / RAND_MAX * 50);  
Snow[i][0] = (double)rand () / RAND_MAX * PWidth; 
OldX = 0; 
OldY = 0; 

SetCtrlAttribute (panelHandle, PANEL_CANVAS, ATTR_PEN_COLOR, VAL_BLACK); 
// 绘制雪花点
CanvasDrawPoint (panelHandle, PANEL_CANVAS, MakePoint (OldX, OldY)); 
// 设置雪花灰度渐变色阶 
colory = 8 * (Snow[i][2] -10); 
colory = 60 + colory;  
RGBValue = MakeColor (colory, colory, colory); 
SetCtrlAttribute (panelHandle, PANEL_CANVAS, ATTR_PEN_COLOR, RGBValue);  
CanvasDrawPoint (panelHandle, PANEL_CANVAS, MakePoint (Snow[i][0], Snow[i][1])); 

break; 
}   
return 0;   
}

(3)程序注释

① NewAsyncTimer 函数

创建一个异步定时器并返回异步定时器ID。创建的异步定时器会自动开辟一个新的线程,其默认优先级为THREAD_PRIORITY_HIGHEST ,如需要修改优先级,可调用NewAsyncTimer WithPriority 函数重新设置优先级,异步定时器支持Windows 、real-time (RT) 、Linux 等系统。函数原型为:

int NewAsyncTimer (double Interval, int Count, int Initial_State, void *Event_Function, void *Callback_Data);

Interval:指定触发事件时间间隔,以秒计。

Count :指定异步定时器触发的事件数目。如果输入负值,则异步定时器只有调用DiscardAsyncTimer 或SuspendAsyncTimerCallbacks 函数时才会被终止。如果输入0 则返回一个错误。

Initial_State :设置异步定时器为运行或暂停状态。可以通过SetAsyncTimerAttribute 函数重新设置状态。

*Callback_Data :用户定义回调数据。

返回值:返回异步定时器ID。负值表示产生错误。错误说明如表4-6 所示。

表4-6 异步定时器错误说明表

错误码

 说明

–1

系统分配异步定时器资源失败

–2

没有ID 资源分配给异步定时器

–3

内存不足

–4

指定ID 不存在

–5

调用并初始化异步定时器未完成

–6

内部错误

–7

参数传递无效

–8

仅用于实时系统

–9

不能设置此属性

*Event_Function :指定用户自定义异步定时器函数名。此参数为AsyncTimerCallbackPtr 函数指针类型,函数定义与普通定时器类似,即:

int CVICALLBACK FunctionName (int reserved, int timerId, int event, void *callbackData, int eventData1, int eventData2);

reserved :保留参数。

timerId :异步定时器ID,用于产生回调事件。

event:响应事件,包含EVENT_TIMER_TICK 和EVENT_DISCARD 事件。

*callbackData :用户定义回调数据。

eventData1 :为双精度指针变量,指向异步定时器持续运行时间。

eventData2 :为双精度指针变量,指向异步定时器从上次触发事件到该时间持续时间。

② DiscardAsyncTimer 函数释放异步定时器资源。函数原型为:

int DiscardAsyncTimer (int Timer_ID);

Timer_ID :异步定时器ID。如果输入值为–1,将释放所有异步定时器资源。注意,创建或释放异步定时器只有当函数执行完毕返回时才能真正完成,否则会有错误产生。

③ SuspendAsyncTimerCallbacks 函数停止所有异步定时器,直到调用ResumeAsyncTimerCallbacks 函数。函数原型为:

int SuspendAsyncTimerCallbacks (void);

④ Delay 函数延时指定时间,以秒计。一般情况下,默认分辨率为1ms 。函数原型为:

void Delay (double Number_of_Seconds);

Number_of_Seconds :延时时间。

⑤ CanvasDrawPoint 函数在Canvas 控件上绘制点图。绘制时使用ATTR_PEN_COLOR 、ATTR_PEN_MODE 和ATTR_PEN_WIDTH 属性。当ATTR_PEN_WIDTH 属性不为1 时,可能出现非圆形点。函数原型为:

int CanvasDrawPoint (int Panel_Handle, int Control_ID, Point Point);

⑥ MakeColor 函数利用红、绿、蓝三色合成一个RGB 颜色值。RGB 值是一个4 字节整型数据,用十六进制表示为0x00RRGGBB,RR、GG、BB 分别表示红、绿、蓝三基色。函数原型为:int MakeColor (int Red, int Green, int Blue);

Red:红色分量值,取值范围为0~255 。

Green:绿色分量值,取值范围为0~255 。Blue:蓝色分量值,取值范围为0~255 。

⑦ DisableBreakOnLibraryErrors 函数

当产生库函数错误时,禁止弹出运行时错误对话框。可以使用SetBreakOnLibraryErrors 函数代替。函数原型为:

void DisableBreakOnLibraryErrors (void);

通常情况下,可以通过选择菜单Run→Break on→Library Errors 选择打开或关闭运行时错误对话框。如果需要打开运行时错误对话框,也可采用EnableBreakOnLibraryErrors 或SetBreakOnLibraryErrors 函数。

⑧ 异步定时器退出问题

如果将面板回调函数PanelCB 中的SuspendAsyncTimerCallbacks 和Delay 函数删除,在关闭面板时,会弹出运行时错误对话框,如图4-8 所示。从提示内容可以看出,此错误为“NON-FATAL RUN-TIME ERROR”,即非致命运行时错误,并给出错误线程ID。由于异步定时器与用户界面逻辑分布在不同的线程中,会造成主函数main 中的DiscardPanel 已经执行完毕,panelHandle 句柄已经释放,但异步定时器可能只运行了一半,由于异步定时器的特殊性决定了其必须运行完毕才能暂停或终止,因此,错误提示中会出现句柄无效的情况。简单的做法是在timer 回调函数的首行添加DisableBreakOnLibraryErrors 函数,禁止错误对话框弹出。如果想根除错误,需要在面板回调函数PanelCB 首先停止异步定时器并等待其执行完毕,即延时一定时间,如一个Interval 来使另一个线程中的timer 函数执行完毕并成功返回。

由于采用多线程技术,同时跟踪多个并发线程是非常必要的。LabWindows/CVI 提供了线程窗口可随时获得每个线程的详细执行信息。在调试状态下,选择LabWindows/CVI 8.5 下的菜单Run→Threads… (或选择LabWindows/CVI 9.0 下的菜单Window→Threads),弹出线程对话框,

如图4-9 所示。在对话框中显示每个线程ID 以及当前执行的函数,选中要查看的线程后,点击“View”按钮,可定位到当前运行的线程。在菜单Run 下,还可以通过Up Call Stack 、Down Call Stack 以及Call Trace… 菜单查询当前线程信息。

edatop.com

图4-8 运行时错误对话框

    

 图4-9 LabWindows/CVI 8.5 线程对话框

(4)运行效果图

点击工具栏中的Debug Project 按钮,程序开始运行,其效果如图4-10 所示。

edatop.com

4-10 运行效果图

点击浏览:矢量网络分析仪、频谱仪、示波器,使用操作培训教程

上一篇:LabWindows/CVI虚拟仪器: 打蜜蜂
下一篇:LabWindows/CVI虚拟仪器游戏设计之:时钟制作

微波射频测量操作培训课程详情>>
射频和天线工程师培训课程详情>>

  网站地图