国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

基于Nokia S60的游戲開發之四

2019-11-18 19:39:57
字體:
來源:轉載
供稿:網友

  應用程序在屏幕上的描畫一般是使用CWsScreenDevice圖形設備來完成,與CWindowGc圖形上下文相關聯。CONE提供了一個CWindowGc實例作為描畫控件的標準圖形上下文。它被CCoeEnv創建并且可以使用CCoeControls::SystemGc方法訪問。CWindowGc的描畫方法在客戶端窗口服務器緩沖區上進行緩沖。

  描畫要么是一個系統初始事務要么是一個應用程序初始事務。系統初始描畫在窗口創建的時候被觸發,或者當窗口內容因為窗口重疊而失效的時候被觸發。對于后一種情況,窗口服務器為每個窗口保持一個無效的區域。如果一個窗口需要重畫,窗口服務器發送一個重畫事件到擁有無效窗口的應用程序中。CONE然后使用無效區域來建立需要被重畫的控件,并且調用它們的Draw方法。這就是為什么每個控件都應該實現Draw方法來重畫它們自己。CCoeControl中的Draw的默認為控件為空。下面的代碼說明了Draw方法的示例:

void CExampleControl::Draw( const TRect& /*aRect*/ ) const
{
 // Get the system graphics context
 CWindowGc& gc = SystemGc();
 // Set drawing settings
 gc.SetBrushStyle( CGraphicsContext::ESolidBrush );
 gc.SetBrushColor( KRgbRed );
 // Draw
 gc.DrawLine( TPoint(10,10), TPoint(30,10) );

  Draw方法的TRect參數指明了需要重畫的無效區域。然而大多數控件忽略矩形,由于它非常簡單并且重新描畫整個控件也不是非常慢。

  當一個應用程序的數據或者狀態改變的時候,需要應用程序初始化描畫,并且屏幕需要更新。CCoeControl提供非虛擬DrawNow方法,指明控件將要描畫的窗口服務器,調用控件的Draw方法,最后指明完成描畫的窗口服務器。CCoeControl還提供了DrawDeferred方法,使窗口無效并且在窗口服務器中觸發一個新的重畫事件。這兩個方法之間的差異是DrawNow強制控件立即重畫自己,而DrawDeferred導致一個重畫事件將使用低優先級操作。由于CONE使用比重畫事件更優先的級別處理用戶輸入事件,所以任何未定的用戶輸入事件都將首先處理。但由于需要重畫整個控件,故都是很繁重的操作,通常只有改變的部分需要重畫,這可以使用下面的代碼做到:

void CExampleControl::DrawBitmap( const TPoint& aPoint,
const CFbsBitmap* aBitmap )
{
 // Get the system graphics context and control rectangle
 CWindowGc& gc = SystemGc();
 // Establish drawing rectangle
 TRect rect = TRect( aPoint,
 TSize( aBitmap.iWidth, aBitmap.iHeight ) );
 // Activate graphics context
 ActivateGc();
 // Invalidate window
 Window().Invalidate( rect );
 Window().BeginRedraw( rect );
 // Draw a bitmap
 gc.DrawBitmap( aPoint, aBitmap );
 Window().EndRedraw();
 // Deactivate graphics context
 DeactivateGc();

  上面的示例代碼在aPoint參數定義的位置畫一個CFbsBitmap。示例中值得注意的是圖形上下文在使用之前需要激活,在描畫完成之后失活。還有窗口服務器需要取得客戶端即將啟動重畫的信息,這使用BeginRedraw方法來完成。由于窗口服務器只允許一個應用程序在無效區域中描畫,所以需要Invalidate方法。在一個系統初始重畫中,CONE激活圖形上下文并且調用用于應用程序的BeginRedraw方法。如果窗口已經無效了,那么Invalidate方法就不必被調用了--這就是為什么系統初始需要被首先描畫。
  子圖形(精靈)

  子圖形是一個經過蒙板化(Mask)的位圖,可以在應用程序不重畫底層窗口的情況下移動。如果游戲不需要經常更新背景,那么使用子圖形就再好不過了。例如類似于PacMan這樣的游戲,在這種游戲中動畫在一個不能卷軸并且固定的背景上移動。重畫是靠窗口服務器來執行的,替代一個較高優先性的任務。這種游戲要考慮的是平滑的動畫和子圖形的運動。Symbian OS提供兩種不同的子圖形:指針和動畫位圖。圖1說明子圖形類的層次。

圖1說明子圖形類的層次

  RWsSPRiteBase是一個用于子圖形的抽象基本類。它擁有一個或多個包含子圖形的位圖數據的TSpriteMembers。通過指定帶有不同的位圖的多個成員,子圖形就可以活動起來了。TSpriteMember還定義了位圖的蒙板,子圖形中位圖的位置和位圖顯示的時間間隔。RWsSprite是一個用于子圖形的具體的類。除了構造器之外,它只提供一個方法SetPosition,可用于移動子圖形。下面的代碼說明了使用從MBM文件中裝載的位圖創建子圖形的示例。

RWsSprite sprite = RWsSprite( iEikonEnv->Wssession() );
User::LeaveIfError( sprite.Construct( Window(), TPoint(0,0), 0 );
for ( TInt i=0; i < 8; i += 2 )
{
 iMember[i/2].iBitmap = new ( ELeave ) CFbsBitmap();
 User::LeaveIfError( member.iBitmap->Load( KBitmapFile, i, EFalse ) );
 iMember[i/2].iBitmap = new ( ELeave ) CFbsBitmap();
 User::LeaveIfError( member.iMaskBitmap->Load( KBitmapFile, i+1, EFalse ) );
 iMember[i/2].iInvertMask = EFalse;
 iMember[i/2].iOffset = TPoint(0,0);
 iMember[i/2].iInterval = TTimeIntervalMicrosecond32(100000);
 User::LeaveIfError( sprite.AppendMember( iMember[i/2] ) );

  在子圖形成員已經更新并且附加到RWsSprite類之后,子圖形可以通過調用RWsSpriteBase::Activate來激活。在此之后,這個子圖形顯示在屏幕上,并且準備移動。子圖形的內容可以使用RWsSpriteBase:UpdateMember方法來變化。因為CFbsBitmaps還可訪問窗口服務器,所以只有子圖形的位圖句柄被發送到窗口服務器。這使子圖形的位圖的更新相當迅速。當子圖形不再需要的時候,窗口服務器需要調用RWsSpriteBase::Close來釋放資源。但不釋放需要被刪除的客戶端成員數據。RWsPointerCursor是一個用于應用程序創建光標的類。

  雙緩沖

  如果一個游戲的圖形由多個需要被經常更新的運動對象組成,窗口服務器的客戶端緩沖可能被充滿并且可能會在所有對象都更新的時候溢出。用戶可能會發現屏幕出現閃爍。如果一個視圖仍然在更新的時候,可能會出現閃爍或者其他不希望的效果。這些問題的解決方案是雙緩沖,圖形先被畫在一個屏外位圖上,然后被畫到屏幕上作為一個單一窗口服務器操作。尤其是對于那種在一秒鐘內重畫幾次屏幕的游戲,使用屏外位圖可以改善它們的性能。

  一個屏外位圖可以使用位圖化的圖形上下文和圖形設備類來創建:CFbsBitGc和CFbsBitmapDevice。它們使用其他的上下文和設備類來創建和使用。為了獲得額外的性能,位圖自己就應該是一個CWsBitmap位圖。在屏外位圖更新之后,它可以使用正常的窗口服務器的描畫方法畫在窗口中。

  當一個應用程序在一個窗口畫位圖時,它轉化為和窗口相同的顯示模式。這是一個很消耗時間的操作,實質上可能降低描畫的速度。因此把位圖用于動畫的游戲應該在動畫開始之前就完成轉化。轉化可以通過使用一個屏外位圖來執行,如下面的示例方法演示:

CFbsBitmap* CExampleControl::LoadAndConvertBitmapL(
Const TDesC& aFileName, TInt aBitmapId )
{
 // Load the bitmap
 CFbsBitmap* originalBitmap = new ( ELeave ) CFbsBitmap();
 CleanupStack::PushL( originalBitmap );
 User::LeaveIfError( originalBitmap->Load( aFileName, aBitmapId, EFalse ) );
 // Create a new bitmap, graphics device and context
 CFbsBitmap* newBitmap = new ( ELeave ) CFbsBitmap();
 CleanupStack::PushL( newBitmap );
 newBitmap->Create( originalBitmap->SizeInPixels(), Window()->DisplayMode() );
 CFbsBitmapDevice* graphicsDevice = CFbsBitmapDevice::NewL(bitmapConverted );
 CleanupStack::PushL( graphicsDevice );
 CFbsBitGc* graphicsContext;
 User::LeaveIfError( graphicsDevice->CreateContext( graphicsContext ) );
 TPoint zero(0,0);

 // Blit the loaded bitmap to the new bitmap
 bitmapContext->BitBlt( zero, originalBitmap );
 CleanupStack::Pop(3);
 delete bitmapContext;
 delete bitmapDevice;
 delete originalBitmap;
 return newBitmap;

  示例方法使用一個文件名和位圖ID作為參數,并且從一個MBM文件中裝載相應的位圖。如果一個游戲有許多位圖應該轉化,那么應該在游戲或者等級的初始化階段轉化。因此用戶就不會看到這個操作了。
  直接描畫

  使用窗口服務器在屏幕上描畫需要一個上下文轉換,這會減慢描畫速度。為了繞過窗口服務器省去繁瑣的上下文轉換,可以直接訪問屏幕。這被稱作直接描畫。在Symbian OS中有兩種方法來直接在屏幕上描畫。

  CFbsScreenDevice是一個可以被發送到屏幕驅動程序SCDV.DLL的圖形設備。在創建一個CFbsBitGc圖形上下文之后,它能像任何其他的圖形設備一樣使用。然而,可以直接在屏幕上描畫,而不需要使用窗口服務器。直接在屏幕上描畫的另一種方法是從系統中查詢屏幕內存地址。這可以使用UserSrv類來實現:

TPckgBuf<TScreenInfoV01> infoPckg;
TScreenInfoV01& screenInfo = infoPckg();
UserSvr::ScreenInfo(infoPckg);
TUint16* screenMemory = screenInfo.iScreenAddress + 16;

  屏幕內存有一個32字節的頭。

  即使在屏幕內存內寫數據比CFbsScreenDevice稍微快一點,但是功能可能根據硬件和屏幕的設備驅動程序的不同而有差異。在一些基于Symbian OS的終端中,屏幕在內存變化的時候自動從屏幕內存中更新,而在其他的終端中描畫需要明確的激活。屏幕內存地址只對目標硬件有效,因此描畫代碼需要分為硬件和模擬器兩部分。在模擬器環境中,可以描畫到一個屏外位圖中,而不是屏幕內存中,然后使用正常的窗口服務器描畫方法位塊傳送到屏幕上。環境可以通過使用__WINS__定義來檢測出來。

#ifdef __WINS__ // Emulator environment
// Draw to an off-screen bitmap
#else // Hardware environment
// Draw directly to the screen memory
#endif 

  這兩種直接描畫方法的一個共同的問題是窗口服務器不了解描畫,因此它不能通知應用程序是否出現另一個窗口或者窗口組。 即使當應用程序失去焦點的時候得到一個事件,它們也不能停止直接描畫,因為直接描畫實在太快了,并且屏幕內容有可能被弄亂。 這可能發生在玩游戲的時候,突然有電話打進來的情況下。
新近的GT 6.1版本提供了一個應用編程接口用于直接描畫,將能解決前面提到的問題。 這個應用編程接口由兩個類組成:一個MDirectScreenaccess類,提供用于應用程序的回調方法,還有一個CDirectScreenAccess類處理與窗口服務器的通訊。 下面的代碼說明CDirectScreenAccess實例是如何構造的,以及直接描畫支持是如何激活的。

iDrawer = CDirectScreenAccess::NewL(iEikonEnv->WsSession(), *iEikonEnv->ScreenDevice(), Window(), *this);
iEikonEnv->WsSession().Flush();
iDrawer->StartL();
iDrawer->ScreenDevice()->SetAutoUpdate(ETrue); 

  CDirectScreenAccess的NewL方法獲得一個窗口服務器會話、CONE的圖形設備、應用程序窗口和一個到MDirectedScreenAccess導出類的指針作為參數。 在CDirectScreenAccess::StartL被調用來激活直接描畫支持之前,客戶端窗口服務器緩沖應該溢出。 為了能自動更新屏幕,屏幕設備的SetAutoUpdate方法需要使用ETrue參數。 當直接描畫支持激活的時候,CDirectScreenAccess產生一個CFbsBitGc圖形上下文,可以被應用程序用來在屏幕上繪畫。

iDrawer->Gc()->BitBlt( TPoint(0,0), iBitmap ); 

  當另一個窗口出現在應用程序窗口上時,CDirectScreenAccess從窗口服務器取得一個事件來中斷描畫。 CDirectScreenAccess然后調用MDirectScreenAccess派生類的AbortNow方法,這個方法必須被應用程序重載以便中斷描畫。 為了防止屏幕被弄亂,窗口服務器直到中斷描畫事件被處理的時候才畫重疊窗口


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 南京市| 北安市| 东山县| 溆浦县| 东乌珠穆沁旗| 正阳县| 尚志市| 彩票| 清涧县| 济源市| 色达县| 平度市| 尼木县| 桦甸市| 滁州市| 新乐市| 江永县| 安岳县| 紫金县| 鄯善县| 宿松县| 西城区| 昌图县| 新沂市| 苗栗市| 宁强县| 凤庆县| 昌邑市| 军事| 察哈| 绥中县| 大同县| 抚远县| 化隆| 旅游| 紫云| 泰州市| 宣威市| 祥云县| 桑植县| 江永县|