摘要
上一回我们为大家介绍了 XNA Framework 支持游戏程序制作的重要类别,做为开发游戏程序的基础知识。除此之外,我们也利用所介绍的类别实作一个简单的游戏程序,并令其具有能够不断卷动的背景图案。这一回我们将会介绍更多 XNA Framework 支持游戏开发的类别,包括支持输入控制,音效播放,以及背景音乐播放控制的类别等等,让读者能够为所制作的游戏程序加入更丰富的游戏效果。
输入控制
游戏程序难免需要和用户互动,让使用者可以藉由输入设备,例如键盘、触控面板、鼠标、或是游戏控制器进行游戏控制,与游戏程序达到互动的效果。要让用户控制游戏程序的进行,可以利用 Keyboard 类别搭配 KeyboardState 结构,利用 TouchPanel 类别搭配 TouchCollection 结构,利用 Mouse 类别搭配 MouseState 结构,或是利用 GamePad 类别搭配 GamePadState 结构,对所操作的游戏程序进行控制。
Keyboard 类别的功能很简单,只提供名称为 GetState 的方法以取得用户操作键盘的按键状态。游戏程序可以呼叫 Keyboard 类别的 GetState 方法取得键盘的状态 (型态为 KeyboardState 结构的传回值),以判断使用者按了那一个键。KeyboardState 结构的常用属性请参考表1 的说明:
属性名称 | 说明 |
Item | 代表键盘的某个按键的按键状态 |
KeyboardState 结构的常用方法请参考表2 的说明:
方法名称 | 说明 |
GetPressedKeys | 取得目前所有被按下的按键 |
IsKeyDown | 判断指定的按键是否已被按下 |
IsKeyUp | 判断指定的按键是否已被放开 |
要判断用户操作鼠标的做法和判断用户操作键盘的操作类别,游戏程序可以先呼叫 Mouse 类别的 GetState 方法取得鼠标的状态 (型态为 MouseState 结构的传回值),再透过鼠标的状态判断用户的操作动作。有关 Mouse 类别常用的方法可以参考表3 的说明:
方法名称 | 说明 |
GetState | 取得包括鼠标坐标在内的鼠标按键状态 |
SetPosition | 设定鼠标光标相对于游戏窗口左上角的位置 |
MouseState 结构常用的属性请参考表4 的说明:
方法名称 | 说明 |
LeftButton | 鼠标左键的按键状态 |
MiddleButton | 鼠标中间键的按键状态 |
RightButton | 鼠标右键的按键状态 |
ScrollWheelValue | 记录从游戏开始执行迄今,鼠标滚轮累计的卷动距离 |
X | 鼠标光标的 X 坐标位置 |
Y | 鼠标光标的 Y 坐标位置 |
虽然 Windows Phone 7 智能型手机并未配备鼠标,但是游戏程序还是仍然能够使用 Mouse 类别与 MouseState 结构判断用户操作鼠标的动作,其效果相当于使用者对智能型手机进行触控操作。GamePad 类别和 GamePadState 结构也一样,虽然 Windows Phone 7 智能型手机未配备游戏控制器,但是游戏程序仍然能够使用 GamePad 类别和 GamePadState 结构判断用户操作游戏控制器的动作,当使用者按下 Windows Phone 7 智能型手机下缘的硬件按键,其效果相当于透过游戏控制器和游戏程序沟通。例如在使用 Visual Studio 2010 Express for Windows Phone 建立的 Windows Phone Game (4.0) 的项目中,Game1 类别的 Update 方法便会利用以下的程序代码判断使用者是否按下智能型手机下缘回到上一个画面的按键,如果是的话,则结束游戏程序的执行:
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) //判斷使用者是否按下回到上一個畫面的按鍵 this.Exit(); //結束遊戲
GamePad 类别常用的方法可以参考表 5 的说明:
方法名称 | 说明 |
GetCapabilities | 查询游戏控制器支持的功能的方法 |
GetState | 取得游戏控制器的状态 |
SetVibration | 设定游戏控制器震动马达的速度 |
GamePadState 结构常用的属性可以参考表6 的说明:
属性名称 | 说明 |
Buttons | 取得游戏控制器所有按键的状态 |
DPad | 取得方向指引器指引的方向 |
IsConnected | 判断游戏控制器是否已连接到游戏设备 |
ThumbSticks | 取得游戏控制器的摇捍的位置 |
Triggers | 取得游戏控制器的触控钮的位置 |
GamePadState 结构常用的方法可以参考表7 的说明:
方法名称 | 说明 |
IsButtonDown | 判断指定的游戏控制器按钮是否被按下 |
IsButtonUp | 判断指定的游戏控制器按钮是否已放开 |
Windows Phone 7 是一只支持多点触控智能型手机,程序设计师可以利用 TouchPanel 类别搭配 TouchCollection 结构判断用户的触控操作。TouchPanel 类别常用的属性可以参考表8 的说明:
属性名称 | 说明 |
DisplayHeight | 代表触控屏幕高度的属性 |
DisplayOrientation | 代表触控屏幕方向的属性 |
DisplayWidth | 代表触控屏幕宽度的属性 |
EnabledGestures | 代表手势操作是否启用的属性 |
IsGestureAvailable | 代表是否支持手势操作的属性 |
TouchPanel 类别常用的方法可以参考表9 的说明:
方法名称 | 说明 |
GetCapabilities | 查询触控面板支持的功能的方法 |
GetState | 取得触控面板目前的状态的方法 |
ReadGesture | 读取用户手势操作的相关信息的方法 |
使用者对 Windows Phone 7 智能型手机进行触控及手势操作的触控信息会存放在 TouchCollection 结构型态的变量中,程序设计师可以利用 TouchCollection 结构提供的属性和方法取得用户的触控和手势操作的相关信息。TouchCollection 结构常的属性可以参考表10 的说明:
属性名称 | 说明 |
Count | 取得触控屏幕被用户触碰的位置的数目 |
IsConnected | 判断触控屏幕是否作用中 |
IsReadOnly | 判断存放触控屏幕被用户触碰的位置的集合是否只读 |
Item | 取得触控屏幕被用户触碰的位置的相关信息 |
TouchCollection 结构常的方法可以参考表11 的说明:
方法名称 | 说明 |
Add | 新增一个触碰点到存放触碰位置的集合中 |
Clear | 清除存放触碰点的集合的所有内容 |
Contains | 清除存放触碰点的集合的所有内容 |
FindById | 依据 ID 搜寻触碰点 |
IndexOf | 判断指定的的触碰点位于存放触碰点的集合的编号 |
Insert | 插入触碰点到存放触碰点的集合的指定位置 |
Remove | 自存放触碰点的集合移除指定的触碰点 |
RemoveAt | 依据编号自存放触碰点的集合移除指定的触碰点 |
了解以 XNA 为基础的游戏的输入控制技巧之后,接下来我们就要制作一个能够允许使用者利用触控的方式移动图形的程序。
首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Window Phone Game (4.0) 型态的项目],并于 Content Pipeline 项目加入一个图形档案 (例如内容为圆形的图形档案),做为用户利用触控面板移动的主体。做好之后请于 Game1 类别中宣告以下的变量,负责管理 2 维图形,以及记录 2 维图形的位置:
Texture2D Ball; //管理 2 維圖形的變數 Vector2 BallPosition; //記錄 2 維圖形的位置
宣告好变量之后请修改 Game1 类别的建构函式,加入设定游戏窗口大小的程序代码,编辑好的 Game1 类别建构函式如下:
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的高度為 800 graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寛度為 480 // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); }
设定好游戏窗口的大小之后请修改 Game1 类别的 LoadContent 方法,负责加载 Content Pipeline 项目中事先准备好的图形,并设定图形要显示在游戏窗口的正中央:
protected override void LoadContent() { //Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Ball = Content.Load<Texture2D>("Ball"); //載入名稱為 Ball 的圖形資源 BallPosition = new Vector2((Window.ClientBounds.Width - Ball.Width) (Window.ClientBounds.Height-Ball.Height)/2); //設定圖形顯示的位置在視窗正中央 }
[提示]
上述的程序代码利用 Game1 类别的 Window 属性取得游戏窗口,再利用游戏窗口的 ClientBounds 属性的 Width 成员和 Height 成员取得游戏窗口的大小,做为计算图形显示位置的依据。
[注意]
读者必须依据准备在 Content Pipeline 项目中的图形资源名称进行加载图形资源的动作。
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here TouchCollection tc=TouchPanel.GetState(); //取得使用者操作觸控面板的狀態 if (tc.Count > 0) //如果使用者有觸控螢幕的動作 { BallPosition = tc[0].Position; //將使用者第一個觸碰點當做顯示圖形的左上角點 } base.Update(gameTime); }
接下来请编辑 Game1 类别的 Draw 方法,将游戏加载的图形资源显示在用户触碰的屏幕坐标点,做好的 Draw 方法如下:
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); //開始繪製遊戲內容 spriteBatch.Draw(Ball, BallPosition, Color.White); //將載入的圖形資源顯示在 BallPosition 變數指定的座標位置 spriteBatch.End(); //結束繪製遊戲內容 base.Draw(gameTime); }
做好之后请按下 CTRL+F5 组合键执行游戏程序,当游戏程序开始执行的时候会将所加载的图形显示在游戏窗口的正中央,当您触碰 Windows Phone 7 智能型手机的触控屏幕的时候,游戏程序显示的图形资源就会改变显示位置到触控屏幕被触碰的坐标点,如图1 所示:
图1:依据用户触碰触控屏幕的坐标点显示图形资源的游戏程序执行的情形
音效与音乐
以 XNA 为基础的游戏程序可以利用 SoundEffect 类别或 SoundEffectInstance 类别播放音效,或是利用 Song 类别和 MediaPlayer 类别执行播放音乐的动作,让游戏程序可以播放悦耳的背景音乐。
在支持音效播放方面,SoundEffectInstance 类别提供更多的音效播放控制方法,而 SoundEffect 类别只能提供单纯的音效播放功能。表12 所列为 SoundEffect 类别常用的属性:
属性名称 | 说明 |
DistanceScale | 距离比例。当音源远离使用者时,如果声音衰减太快,则须递增 DistanceScale 属性的内容值,反之则须递减 DistanceScale 属性的内容值 |
DopplerScale | Doppler 比例。当音源接近用户时,如果频率升高太快,则须递减 DopplerScale 属性的内容值,反之则须递增 DopplerScale 属性的内容值 |
Duration | 控制音效播放时间长短的属性 |
MasterVolume | 代表音量大小的属性。内容值为 0.0f 代表不发出声音 (静音),内容值为 1.0f 代表最大音量 |
SpeedOfSound | 代表音速的属性,内容值为每秒 343.5 公尺 |
表13 所列为 SoundEffect 类别常用的方法:
方法名称 | 说明 |
CreateInstance | 能够利用 SoundEffect 类别的对象建立 SoundEffectInstance 类别的对象的方法 |
FromStream | 能够利用数据流建立 SoundEffect 类别的对象 |
Play | 负责播放音效的方法 |
SoundEffect 类别支持控制音效播放的功能较少,例如只支持播放音效的 Play 方法而未支持停止音效播放 Stop 方法,而且也未提供重复播放音效的功能,如果游戏程序需要对所播放的音效执行进阶控制,就可以利用 SoundEffect 类别提供的 CreateInstance 方法建立 SoundEffectInstance 类别的对象,以便对所播放的音效执行更进阶的控制。
表14 所示即为 SoundEffectInstance 类别常用的属性:
属性名称 | 说明 |
IsLooped | 控制音效是否需要重复播放的属性 |
Pan | 控制音效平移的属性 |
Pitch | 控制音效的音调高低的属性 |
State | 代表音效播放状态的属性。其内容值为 Paused、Playing、Stopped 三种状态之一 |
Volume | 控制音效播放音量的属性 |
有关 SoundEffectInstance 类别常用的方法可以参考表15 的说明:
方法名称 | 说明 |
Apply3D | 播放 3D 音效 |
Pause | 暂停音效播放 |
Play | 播放音效 |
Resume | 恢复音效播放 |
Stop | 停止音效播放 |
如果游戏程序欲播放的是音乐,例如扩展名为 mp3 的音乐或歌曲当做游戏的背景音乐,就必须用到 Song 类别和 MediaPlayer 类别。Song 类别负责管理游戏程序欲播放的音乐或歌曲,其常用的属性请参考表16 的说明:
属性名称 | 说明 |
Duration | 代表歌曲长度的属性 |
IsProtected | 代表音乐是否为数字版权管理 (DRM) 的内容 |
Song 类别常用的方法可以参考表17 的说明:
方法名称 | 说明 |
FromUri | 利用 Uri 指向的音乐建立 Song 类别的对象 |
建立好负责管理音乐或歌曲的 Song 类别的对象之后,就可以利用 MediaPlayer 类别进行播放与控制。MediaPlayer 类别常用的属性可以参考表18 的说明:
属性名称 | 说明 |
IsMuted | 控制是否要调整成静音模式的属性 |
IsRepeating | 控制是否要重复播放音乐的属性 |
PlayPosition | 取得目前播放位置的属性 |
State | 代表目前播放状态的属性。其内容值为 Paused、Playing、Stopped 三种状态之一 |
Volume | 控制播放音量的属性 |
MediaPlayer 类别常用的方法可以参考表19 的说明:
方法名称 | 说明 |
Pause | 暂停音乐播放 |
Play | 播放音乐 |
Resume | 恢复音乐播放 |
Stop | 停止音乐播放 |
为游戏程序加入音效和背景音乐
了解以 XNA 为基础的游戏程序支持播放音效与音乐的基本功能之后,接下来我们就要为所开发的游戏程序加入背景音乐。
首先请为 [Windows Phone Game (4.0)] 项目中的 Content Pipeline 项目加入当做背景音乐与音效的资源文件,例如您可以为项目加入扩展名为 .wma 的档案当做游戏程序的音效资源,为项目加入扩展名为 .mp3 或 .wma 的档案当做游戏的背景音乐。有关 Content Pipeline 项目支持的音效与音乐资源格式可以参考 [认识 XNA Game Studio 4.0] 一文的说明。
加入妥音效资源和背景音乐资源之后,请于 Game1 类别加入以下的变量宣告,负责管理音效资源,播放音效的 SoundEffectInstance 类别的对象,以及管理背景音乐的 Song 类别的对象:
SoundEffect HitWall; //管理球體碰撞到遊戲視窗邊界的音效的變數 SoundEffectInstance HitWallEffect; //控制音效播放的變數 Song BackgroundSong; //管理遊戲背景音效的變數
接下来请编辑 Game1 类别的 LoadContent 方法,加入以下的程序代码,执行加载音效和音乐资源的动作:
HitWall = Content.Load<SoundEffect>("HitWall"); //載入名稱為 HitWall 的音效資源 HitWallEffect = HitWall.CreateInstance(); //建立播放音效資源的 SoundEffectInstance 物件 HitWallEffect.Apply3D(new AudioListener(), new AudioEmitter()); //呼叫 SoundEffectInstance 物件的 Apply3D 方法,播放 3D 音效 BackgroundSong = Content.Load<Song>("SuperMario");//載入名稱為 SuperMario 的背景音樂資源 MediaPlayer.IsRepeating = true; //將MediaPlayer類別的IsRepeating屬性設定成true,表示要不中斷地重覆播放背景音樂 MediaPlayer.Play( BackgroundSong); //呼叫 MediaPlayer 類別的 Play 方法播放名稱為 //BackgroundSong的物件管理的背景音樂
最后请编辑 Game1 类别的 Update 方法,加入以下的程序代码,以便在使用者利用触碰触控屏幕移动游戏程序显示的图形时,判断所移动的图形是否碰撞到游戏窗口的边界,以决定是否要播放碰撞的音效:
if (BallPosition.X == 0 || BallPosition.X > Window.ClientBounds.Width - Ball.Width || BallPosition.Y==0 || BallPosition.Y > Window.ClientBounds.Height-Ball.Height) //判斷圖形是否碰撞到遊戲視窗的四個邊界 { HitWallEffect.Play(); //播放碰撞的音效 }
[说明]
控制播放音效与背景音乐的动作属于控制游戏程序状态的范畴,和显示游戏程序内容无关,所以不需要修改 Game1 类别的 Draw 方法。
做好之后请按下 CTRL+F5 组合键执行项目,待游戏程序启动之后,读者就可以听到不断重复播放的背景音乐。当您触碰游戏程序的窗口以移动戏程显示的图形时,只要图形碰触到游戏窗口的边界,就会听到游戏程序发出的碰撞音效。
范例下载:TouchMoveBall.zip
[结语]
这一回我们为大家介绍了更多的 XNA Framework 支持游戏开发的类别,包括支持输入控制,音效播放,以及背景音乐播放控制的类别等等,让读者能够为所制作的游戏程序加入更丰富的游戏效果。下一回我们将要为大家介绍进阶的输入控制,例如手势触控输入,进阶音效控制技巧,碰撞侦测,以及辅助 XNA 游戏设计的好用类别。