调整和改编赛车游戏——游戏屏幕
游戲屏幕
賽車游戲中有很多不同的游戲屏幕,這些都是由RacingGame類中的gameScreens堆棧管理的。本節介紹游戲中使用的大部分屏幕和對應的功能。大多數游戲屏幕相當簡單,但其他的有點復雜并實現了一個單元測試,通過單元測試能更好地了解這個類。例如,Credits屏幕是相當簡單的,它只顯示一個背景紋理,但主菜單很復雜并具有所有能進入另一個屏幕的按鈕。Options屏幕介紹了許多新的控制選項,這些都要進行測試,這個類里有一個單元測試能幫你完成這個過程。
所有的游戲屏幕類都繼承于IgameScreen接口(見圖14-6),您也許還記得在第8章的Rocket Commander中也使用過。幾乎沒有改變,但更簡單了。您現在只有一個叫做Render的方法,它沒有參數并返回一個布爾值。該代碼和你在XNA Shooter中使用的幾乎一樣。如果Render方法返回true意味著您可以返回到以前的游戲屏幕,一旦推出最后一個游戲屏幕則游戲推出。通常游戲屏幕返回false,因為玩家再次進入游戲屏幕后不會立即退出。
圖 14-6
由于這個簡單的接口,所以所有的游戲屏幕的類視圖幾乎是一樣的,它們都只有一個Render方法。有些類還會有一些私有的輔助方法,但它們大多是非常簡單的。一些更復雜的游戲屏幕還有單元測試用以測試是否所有內容和功能都能正確實行。處理整個游戲的GameScreen類是復雜的。不像XNA Shooeter或Rocket Commander,所有游戲代碼的的處理和渲染是在Landscape和model類中的。游戲邏輯在Player類中被處理,這個有點復雜,但你已經在上一章學習了所有的CarPhysics類的基本物理知識和ChaseCamera類。
看看圖14-7了解一下游戲屏幕的基本概況。最復雜的類顯然是Mission類,它處理游戲過程和游戲邏輯。它不包含所有的游戲代碼,有些是在Player類和RacingGame類中被處理的。
圖 14-7
啟動屏幕
啟動屏幕(見圖14-8)是較容易的一個類,它只是等著玩家按下手柄上的Start。如果沒有手柄,Space或Esc鍵或鼠標左鍵也可讓玩家繼續。這個類唯一令人感興趣的地方就是讓“Press Start to continue”文字閃爍的代碼。
圖 14-8
RenderMenuBackground方法有點復雜。它用來顯示菜單背景,即顯示出汽車通過一條賽道。汽車是由計算機控制的而相機只是跟隨它。代碼不是很復雜:
// [From RenderMenuTrackBackground(), which is called by // RenderMenuBackground(), both located in the UIRenderer class]// [Some code to calculate carPos, carMatrix, etc.]// Put camera behind car RacingGame.Player.SetCameraPosition(carPos + carMatrix.Forward * 9 - carMatrix.Up * 2.3f);// For rendering rotate car to stay correctly on the road carMatrix =Matrix.CreateRotationX(MathHelper.Pi / 2.0f) *Matrix.CreateRotationZ(MathHelper.Pi) *carMatrix;RacingGame.Landscape.Render(); RacingGame.CarModel.RenderCar(randomCarNumber, randomCarColor, carMatrix);借助于Landscape類的Render方法和Model類的RenderCar方法您不必擔心渲染場景、賽道或其他東西。相機矩陣可以確保你在正確的位置上觀看,而賽車通過汽車矩陣在賽道上行駛。.
所有其他游戲屏幕菜單也使用RenderMenuBackground方法顯示屏幕背景,但在一些游戲屏幕上不太明顯,因為你把一個較暗的紋理放在前面(例如,在Credits屏幕上很難看到背景)。這只是一個背景效果,當你正式開始玩游戲時你可以看到更多的屏幕。
主菜單
主菜單(見圖14-9)比其他菜單屏幕復雜點,但即使這樣這個類也只有約250行代碼。除了啟動屏幕,其他屏幕都是從這里開始的。最重要的選項是開始游戲和觀看highscores (頭兩個按鈕)。
圖 14-9
這個類最酷的功能是菜單按鈕的動畫。每個按鈕獲得一個介于0和1之間的浮點值,其中0代表按鈕尺寸的最小可能值而1代表最大。當鼠標懸浮或用手柄、鍵盤選擇按鈕時,它將慢慢變大直到達到1.0。當你離開按鈕,它又緩慢變小。
第一個按鈕初始設為1,其他按鈕設為最小尺寸(0)。
/// <summary> /// Current button sizes for scaling up/down smooth effect. /// </summary> float[] currentButtonSizes =new float[NumberOfButtons] { 1, 0, 0, 0, 0, 0 };然后在Rnnder方法中處理按鈕選擇和縮放。為了確保你會在同時懸浮在一個以上的按鈕,這里為鼠標使用了一個輔助變量。如果不使用鼠標選取菜單按鈕,這個變量將不會被使用。
// Little helper to keep track if mouse is actually over a button. // Required because buttons are selected even when not hovering over // them for GamePad support, but we still want the mouse only to // be applied when we are actually over the button. int mouseIsOverButton = -1;// [a little later in the code ...] for (int num = 0; num < NumberOfButtons; num++) {// Is this button currently selected?bool selected = num == selectedButton;// Increase size if selected, decrease otherwisecurrentButtonSizes[num] +=(selected ? 1 : -1) * BaseGame.MoveFactorPerSecond * 2;if (currentButtonSizes[num] < 0)currentButtonSizes[num] = 0;if (currentButtonSizes[num] > 1)currentButtonSizes[num] = 1;// Use this size to build rectRectangle thisRect =InterpolateRect(activeRect, inactiveRect, currentButtonSizes[num]);Rectangle renderRect = new Rectangle(xPos, yPos - (thisRect.Height - inactiveRect.Height) / 2,thisRect.Width, thisRect.Height);BaseGame.UI.Buttons.RenderOnScreen(renderRect, ButtonRects[num],// Make button gray if not selectedselected ? Color.White : new Color(192, 192, 192, 192));// Add border effect if selectedif (selected)BaseGame.UI.Buttons.RenderOnScreen(renderRect,UIRenderer.MenuButtonSelectionGfxRect);// Also check if the user hovers with the mouse over this buttonif (Input.MouseInBox(renderRect))mouseIsOverButton = num;// [etc.] } // for (num)if (mouseIsOverButton >= 0)selectedButton = mouseIsOverButton;Game Screen
GameScreen類(見圖14-10)是最重要的游戲屏幕,因為它處理整個游戲邏輯。游戲變量不儲存在這個類,但所有的重要組成部分(場景,賽道,汽車,對象,HUD等)從這里被渲染和調用。
圖 14-10
大多數玩家變量都存儲在Player類,所有的輸入和物理在Player的基類中(CarPhysics和ChaseCamera)處理。Player類還使用了所有的游戲變量比賽。大多數變量顯示在用戶界面中,比如在HUD上的目前的游戲時間,它們在Player類的HandleGameLogic方法中被更新。
所有Render方法,包括HUD,都在UIRenderer輔助類中處理,其余的在Landscape類和model類中處理和渲染,陰影映射也在那里進行。所有的渲染和游戲處理是從這里調用,所以這個類為您提供了游戲中發生了什么的一個很好的概括。如果你編寫一個改編版本,那么從這開始修改代碼。在這或調用方法中注釋掉代碼,能很快地看到游戲的哪一部分受到了影響。
Render方法的第一部分處理所有的陰影映射,這不是很復雜,因為大多數已在Landscape類中被處理了。你只需將數據提供給陰影映射類,這個類渲染所有陰影映射,這些陰影映射能直接被使用。
/// <summary> /// Render game screen. Called each frame. /// </summary> public bool Render() { if (BaseGame.AllowShadowMapping) {// Generate shadowsShaderEffect.shadowMapping.GenerateShadows(delegate{RacingGame.Landscape.GenerateShadow();RacingGame.CarModel.GenerateShadow(RacingGame.Player.CarRenderMatrix);});// Render shadowsShaderEffect.shadowMapping.RenderShadows(delegate{RacingGame.Landscape.UseShadow();RacingGame.CarModel.UseShadow(RacingGame.Player.CarRenderMatrix);}); } // if (BaseGame.AllowShadowMapping)然后開始post-screen glow shader并渲染所有的3D內容。這包括天空盒,帶有賽道的場景和所有三維模型,最后是汽車。
// This starts both menu and in game post screen shader! BaseGame.UI.PostScreenGlowShader.Start();// Render background sky and lensflare. BaseGame.UI.RenderGameBackground();// Render landscape with track and all objects RacingGame.Landscape.Render();// Render car with matrix we got from CarPhysics RacingGame.CarModel.RenderCar(RacingGame.currentCarNumber, RacingGame.CarColor,RacingGame.Player.CarRenderMatrix);// And flush all models to be rendered BaseGame.MeshRenderManager.Render();在MeshRenderManager渲染所有的三維模型后您可以添加陰影映射效果。這里的調用順序是重要的,因為如果在顯示陰影前還沒有渲染三維模型,陰影將不正確或不工作。
// Show shadows we calculated above if (BaseGame.AllowShadowMapping) {ShaderEffect.shadowMapping.ShowShadows(); } // if (BaseGame.AllowShadowMapping)// Apply post screen shader here before doing the UI BaseGame.UI.PostScreenGlowShader.Show();代碼的最后是游戲的用戶界面,如果你想去除或改變HUD,在這里做這件事。
// Play motor sound Sound.UpdateGearSound(RacingGame.Player.Speed,RacingGame.Player.Acceleration);// Show on screen UI for the game. BaseGame.UI.RenderGameUI((int)RacingGame.Player.GameTimeMiliseconds,// Best time and current lap(int)RacingGame.Player.BestTimeMs,RacingGame.Player.CurrentLap+1,RacingGame.Player.Speed * CarPhysics.MeterPerSecToMph,// Gear logic with sound (could be improved ^^)1+(int)(5*RacingGame.Player.Speed/CarPhysics.MaxSpeed),// Motormeter0.5f*RacingGame.Player.Speed/CarPhysics.MaxSpeed +// This could be improved0.5f*RacingGame.Player.Acceleration,RacingGame.Landscape.CurrentTrackName,Highscore.GetTop5Highscores());if (Input.KeyboardEscapeJustPressed ||Input.GamePadBackJustPressed) {// Stop motor soundSound.StopGearSound();// Play menu music againSound.Play(Sound.Sounds.MenuMusic);// Return to menureturn true; } // if (Input.KeyboardEscapeJustPressed)return false; } // Render()Highscores
Highscores屏幕(見圖14-11)非常相似與Rocket Commander的,但所有在線highscores被移除,因為沒有實現網絡代碼或Web服務。原因仍是XNA缺乏網絡支持,但在PC版本中仍有可能實現。
圖 14-11
在Highscores類中有幾個輔助方法,例如,幫助您確定當前在游戲中的排名,但大多數方法已經這本書的前面幾個游戲中用到過了。
下面的代碼是用來顯示排行榜前10名的玩家。你會發現,借助于UIRenderer類中的輔助方法代碼是很簡單的。如果您只想測試Highscores游戲屏幕,請使用類內部的單元測試,這是用來定位所有的用戶界面元素的,此游戲的其他屏幕也以同樣的方式做這件事。我也在Visual Studio 2005使用了TestDriven.NET,它能通過熱鍵重新運行測試。這樣我可以測試代碼,按下熱鍵,說:“哦,不,”,按下Escape,并修復代碼,直到類工作正常。大多數的UI代碼都是通過這種方式進行測試的。
// Go through all highscores for (int num = 0; num < NumOfHighscores; num++) {// Show player in white if mouse is over line or else use gray colorRectangle lineRect = new Rectangle(0, yPos, BaseGame.Width, lineHeight);Color col = Input.MouseInBox(lineRect) ?Color.White : new Color(200, 200, 200);// Fill in text for this lineBaseGame.UI.WriteText(xPos1, yPos, (1 + num) + ".", col);BaseGame.UI.WriteText(xPos2, yPos,highscores[selectedLevel, num].name, col);BaseGame.UI.WriteGameTime(xPos3, yPos,highscores[selectedLevel, num].timeMs, Color.Yellow);yPos += lineHeight; } // for (num)剩下的其他游戲屏幕類是Option,Help和Credit。它們和highscores類非常相似,并不令人興奮。Option有一些不錯的UI功能,可以讓你在Input類的幫助下輸入文字,選取其中一個或多個滑塊并拖動它們。使用Option類的單元測試更多地了解這些功能。Help和Credit類只是在屏幕上顯示一個紋理,非常類似于你先前看到的SplashScreen類。
最后,點擊退出按鈕可以退出游戲,因為主菜單關閉后將不會有其他游戲屏幕了。而所有其他游戲屏幕總是返回到主菜單(包括SplashScreen類)。
轉載于:https://www.cnblogs.com/AlexCheng/archive/2010/10/17/2120185.html
總結
以上是生活随笔為你收集整理的调整和改编赛车游戏——游戏屏幕的全部內容,希望文章能夠幫你解決所遇到的問題。