Currently, mobile phone game APIs (GAPI) provide powerful and efficient programming interfaces for game developers on mobile phones. Of course, Gapi is not only used in games, gapi can be used in areas requiring efficient graphic display and processing.
Gapi is based on the dynamic connection library method. applications directly call functions in the dynamic library. Generally, the Gapi library file name is GX. dll. Currently, the mobile phone provides the GX. dll file.
A typical game or application uses the following Gapi functions:
Opendisplay (fullscreenflag)
Enable the Gapi display function.
Openinput
Enable the function of directly responding to input messages on the hardware keyboard
Getdisplayproperties
Obtain vfb detailed structure information
Getdefaultkeys
Obtain the default key value
Manipulate Gapi
To write a game, you must first enable the Gapi display function to obtain the control permission for controlling the video display cache. Yes
Gxopendisplay (hwnd, DWORD dwflags)
The hwnd parameter is the window handle of the game program. dwflags defines the display mode, and the macro gx_fullscreen defines the full screen mode to control the full screen area of the device. The value 1 indicates that the request is successfully opened, and the value 0 indicates that the request fails.
Although mobile phone systems are used, different series of products may use different display devices. Therefore, different display devices may have different display performance parameters and resolution rates, different colors show different colors. These issues have to be considered when writing a game program running for the mobile phone series, so that the program can adapt to different display environments to achieve the desired Display Effect of the program.
How do I obtain the relevant display information? You can call the following functions:
Gxdll_api gxdisplayproperties gxgetdisplayproperties ();
It can display all the device-related details, which are required when developing a Gapi-based game. All information is returned to the gxdisplayproperties structure. Its structure is as follows:
Struct gxdisplayproperties {
DWORD cxwidth;
DWORD cyheight;
Long cbxpitch;
Long cbypitch;
Long cbpp;
DWORD ffformat;
};
This structure provides the display device information, that is, the parameter indicators of the current video cache area.
Cxwidth and cyheight are the pixel values that display the device width and height. They provide the number of pixels that can be displayed horizontally and vertically. cbpp is the number of digits required by each pixel and is always equal to the power of N2. Cbxpitch and cbypitch provide the number of bytes that differ from memory data between two adjacent pixels. cbxpitch indicates the difference between the two pixels. When cbpp is greater than or equal to 8, cbxpitch indicates the number of bytes of the difference. When cbpp is smaller than 8, cbxpitch cannot actually reflect the number of bytes of the difference. In fact, you must calculate the adjacent address by yourself:
For example:
Cbpp = 4;
Leftpointaddr = Pb + (current_x + 1) * cbpp)> 3)
+ (Current_y * cbypitch)
Cbypitch indicates the difference between two pixels in the up and down directions. The address of the pixel from the up to the up and down directions is calculated by adding or subtracting cbypitch:
Downpointaddr = currentpointaddr + cbypitch;
The ffformat parameter specifies the processing method and format of the color depth of the display device:
If ffformat is equal to kflandscape, the current display mode is landscape screen, that is, the origin () is changed to the lower left corner.
Ffformat is equal to kfpalette, indicating that color display is based on the color palette.
Ffformat is equal to kfdirect, indicating that color display is a direct ing without referencing the color palette.
Ffformat equals to kfdirectinverted, indicating that the color is reversed.
Ffformat equals to kfdirect555, kfdirect565, and kfdirect888 indicate the bit information occupied by Red, green, and blue when the ing color is displayed.
The method for calculating the coordinates of each pixel is as follows (x, y ):
Unsigned char * pb;
If (cbpp <8 ){
Address = Pb + (x * cbpp)> 3) + (y * cbypitch)
}
Else
{
Address = Pb + (x * cbxpitch) + (y * cbypitch );
}
Determine whether the device is a standard display device
You can use the gxisdisplaydrambuffer () function. If the returned value is true, it indicates a non-standard display device. If the returned value is flase, it indicates a standard display device. When it is a non-standard display device, you need to use the gxsetviewport function to define the area of the display screen. gxsetviewport is invalid on the standard display device.
Gxdll_api int gxsetviewport (
DWORD dwtop,
DWORD dwheight,
DWORD dwreserved1,
DWORD dwreserved2)
Dwtop defines the Y coordinate of the area displayed on the screen. dwheight indicates the height of the area. dwreserved1 and dwreserved are retained and must be set to 0.
Start to draw pixels
Now you can prepare to draw images for operations in the cache area. You can use gxbegindraw to get the first address of the cache area:
Void * gxbegindraw ();
The Return Value of the function is the required first address. If it is null, the cache zone is not displayed. Then you can perform some column pixel operations. After the operation, you need to call gxenddraw to complete the operation:
Int gxenddraw ();
If the return value is 1, the call is successful. If the return value is 0, the call is incorrect.
Submit the drawn information. The changed image has taken effect. When the program loses focus, it must call gxsuspend () to suspend all Gapi operations and hand over the screen control to other programs. When receiving the focus information, the program must call gxresume () so that the program continues to run Gapi functions.
When you exit the program, you must release the Gapi resources. You can call:
Int gxclosedisplay ();
Gapi efficient texture
When developing image processing or games, we can use GDI to produce satisfactory products. However, when developing complex and high-speed graphic display or highly efficient dynamic games, it is often frustrating that the display efficiency of GDI is not high. Although dual cache and other technologies can be used, the interface of the GDI layer is low in efficiency and cannot meet the requirements.
Gapi performs direct operations on the display cache to greatly improve the display efficiency. Therefore, Gapi is well deserved when mobile phone needs to process high-speed textures.
Although Gapi is highly efficient and powerful, it provides direct read and write permissions for the display cache, but based on such a low-level function, when writing a slightly complex program, it takes a lot of time and effort to process operations on the display cache, because this cache is not as easy as the drawing cache provided by GDI for image display, writing several pages of code is required to display a BMP image, which is very annoying.
This section describes the next third-party texture class stgapibuffer written using Gapi. Stgapibuffer provides an interface similar to the GDI Method for simple operations. It is easy to construct the cache content to be displayed, and simply copy the content to the display cache, it is displayed.
You can simply add stgapibuffer. h and stgapibuffer. cpp to the project.
To draw a jpg image, you must first add the image to this class and create the data suitable for stgapibuffer processing. To use the createnativebitmap function, perform the following operations:
Hbitmap = shloaddibitmap (_ T ("// image.bmp ");
G_pnativebitmap = g_gapibuffer.createnativebitmap (hbitmap );
: Eleteobject (hbitmap );
Next, you need to set the target object for the painting. It can be another stgapibuffer cache, or the display device cache. Call the setbuffer function. The Code is as follows:
G_gapibuffer.setbuffer (pdisplaybuffer );
It is best to use cstgapibuffer: bitblt to draw the required data to the cache area. Similar to the bitblt function of GDI, the Code is as follows:
G_gapibuffer.bitblt (0, 0,100,100, g_pnativebitmap );
In this way, an image is displayed on the screen.
Cstgapibuffer also provides a function to draw transparent images. This is often the case when drawing. Use cstgapibuffer: maskedblt to easily draw a pattern with a specified transparent color. Of course, I often use creatememorydc to create a temporary memory DC for drawing when using GDI. cstgapibuffer also provides a function similar to cstgapibuffer: creatememorybuffer.
Example of cstgapibuffer class (partial code ):
Cnativebitmap * pasteroidbitmap = NULL;
Cnativebitmap * pasteroidmask = NULL;
Cstgapibuffer gapibufferbackground; // background
Cstgapibuffer gapibuffermemory;
Cstgapibuffer gapibufferscreen;
Hbitmap hbackground =: loadbitmap (hinst, makeintresource (idb_background ));
Cnativebitmap * pbackgroundbitmap =
Gapibuffermemory. createnativebitmap (hbackground );
: Eleteobject (hbackground );
Gapibufferbackground. creatememorybuffer ();
Gapibufferbackground. bitblt (0, 20, dwdispwidth, dwdispheight,
Pbackgroundbitmap );
Delete pbackgroundbitmap;
Pbackgroundbitmap = NULL;
////////////////////////////////////
Hbitmap hasteroid =: loadbitmap (hinst, makeintresource (idb_asteroid ));
Pasteroidbitmap = gapibuffermemory. createnativebitmap (hasteroid );
: Eleteobject (hasteroid );
Hbitmap hasteroidmask =: loadbitmap (hinst, makeintresource (idb_asteroid_mask ));
Pasteroidmask = gapibuffermemory. createnativebitmap (hasteroidmask );
: Deleteobject (hasteroidmask );
/////////////////////////////////////////
//
Dwtransparentcolor = gapibuffermemory. getnativecolor (RGB (249, 57,198 ));
// Create an offscreen Buffer
Gapibuffermemory. creatememorybuffer ();
Gapibuffermemory. bitblt (& gapibufferbackground );
//////////////////////////////////////// ///////////
//
Gapibuffermemory. transparentbltex (0, noffset % dwdispheight,
50, 60, pasteroidbitmap, dwtransparentcolor );
Gapibuffermemory. transparentbltex (100, (noffset * 2) % dwdispheight,
50, 60, pasteroidbitmap, dwtransparentcolor );
Gapibuffermemory. transparentbltex (noffset % dwdispwidth,
50, 50, 60, pasteroidbitmap, dwtransparentcolor );
Gapibuffermemory. transparentbltex (noffset * 2) % dwdispwidth,
150, 50, 60, pasteroidbitmap, dwtransparentcolor );
Gapibuffermemory. maskedblt (dwdispwidth-(noffset * 2) % dwdispwidth,
(Noffset * 8) % dwdispheight, 50, 60, pasteroidbitmap, pasteroidmask );
Gapibuffermemory. maskedblt (noffset % dwdispwidth * 5), (noffset * 3) %
Dwdispheight, 50, 60, pasteroidbitmap, pasteroidmask );
Gapibuffermemory. maskedblt (noffset * 3) % dwdispwidth, (noffset * 4) %
Dwdispheight, 50, 60, pasteroidbitmap, pasteroidmask );
Gapibuffermemory. maskedblt (noffset % dwdispwidth * 2), dwdispheight-
(Noffset * 5) % dwdispheight, 50, 60, pasteroidbitmap, pasteroidmask );
Rect rc = {0, 20, dwdispwidth/3, 10 };
Fillrect (& rc, RGB (255, 0, 0 ));
RC. Left = dwdispwidth/3;
RC. Right = 2 * dwdispwidth/3;
Fillrect (& rc, RGB (0,255, 0 ));
RC. Left = 2 * dwdispwidth/3;
RC. Right = dwdispwidth;
Fillrect (& rc, RGB (0, 0,255 ));
Void * pbuffer = gxbegindraw ();
Gapibufferscreen. setbuffer (pbuffer );
Gapibufferscreen. bitblt (& gapibuffermemory );
Gxenddraw ();
Gapi Keyboard Message
Use the gxopeninput () function to obtain control of the keyboard. Call the gxgetdefaultkeys (gx_normalkeys) function to obtain the message ing of the default keyboard. Then, in the Windows message processing function, we can receive the keyboard message sent by Gapi. When you press a key, the program will receive the wm_keydown, the wparam parameter contains the message of the key mapped by Gapi. It can be used with the field definition in the obtained gxkeylist structure to determine whether the received key is a defined function key.
The gxkeylist structure is as follows:
Struct gxkeylist {
Short vkup;
Point ptup;
Short vkdown;
Point ptdown;
Short vkleft;
Point ptleft;
Short vkright;
Point ptright;
Short vka;
Point PTA;
Short VKB;
Point PTB;
Short VKC;
Point PTC;
Short vkstart;
Point ptstart;
};
Game Vibration
Providing a vibrating effect in a game can make players very excited and increase the attraction of the game. The mobile phone SDK provides an API to control the vibration effect, which is as simple as controlling the sound.
Obtain vibration device attributes
To learn whether the mobile phone supports vibration, vibration performance, and current settings, you can call the following function:
Int vibrategetdevicecaps (vibratedevicecaps VDC );
Vibratedevicecaps is an enumeration type with the following structure:
Typedef Enum {
Vdc_ampl,
Vdc_frequency,
Vdc_last
} Vibratedevicecaps
Vdc_ampl query the amplitude supported by the vibration device
Vdc_frequency query the vibration frequency supported by the vibration device
Vdc_last query the amplitude supported by the vibration device
If the function succeeds, it returns numbers 0 to 7. numbers 0 indicate that the device does not provide the vibration function. 1 indicate that the device has the vibration function and can be used, but only enables or disables the vibration function, the Vibration cannot be adjusted. 2 to 7 indicate that the device provides different levels of vibration function, and the larger the number, the stronger the adjustment capability. When the device has different levels of vibration capabilities, I can use the vibratenote structure for detailed settings.
How can we start using the vibration function? The mobile phone SDK provides the vibrate function:
Hresult vibrate (
Dword cvn,
Const vibratenote * rgvn,
Bool frepeat,
DWORD dwtimeout
);
It provides different amplitude, different frequency, and can adjust the vibration time. The CVN parameter is the dimension of the second rgvn array, and rgvn is a pointer to a group of vibratenote structures.
The vibratenote structure is as follows:
Typedef struct {
Word wduration;
Byte bamplitude;
Byte bfrequency;
} Vibratenote
Wduration indicates the duration of the vibration. bamplicate defines the amplitude of the vibration and allows the value range from 0 to 7. If the value is 0 x ff and the default value is used as the parameter, bfrequency defines the vibration frequency, 0-7 levels are allowed. If 0xff is used as a parameter, the default value is used.
When you need to stop the vibration of course, you can call the vibratestop () function. If s_ OK is returned, the call is successful. If e_fail is returned, the call fails.
The following is a sample code:
Int caps =-1;
Caps = vibrategetdevicecaps (vdc_amplaps );
If (CAPS <= 0)
Return false; // An error occurred while returning the amplitude, indicating that the vibration function is not supported.
Hresult hR = vibrate (0, null, true, infinite); // No time limit
If (hR = e_fail)
{
MessageBox (null, l "e_fail", l "", mb_ OK );
}
Else if (hR = e_notimpl)
{
MessageBox (null, l "e_notimpl", l "", mb_ OK );
}
Sleep (1000); // time spent on Vibration
Vibratestop ();
Start the first mobile game
Here, Gapi is used to simulate a greedy game, which is very simple. The main focus is on how to use Gapi, how to demonstrate operations on the video cache area during use, and not beautify the appearance.
Initialize the Gapi library. In the initinstance function, we initialize the Gapi display and input.
If (gxopendisplay (hwnd, gx_fullscreen) = 0)
Return false;
Gx_displayprop = gxgetdisplayproperties ();
If (gx_displayprop.cbpp! = 16)
{
// Only dealing with 16 bit color in this Code
Gxclosedisplay ();
MessageBox (hwnd, l "sorry, only supporting 16bit color", l "Sorry! ", Mb_ OK );
Return false;
}
Framebuf = (unsigned short *) malloc (sizeof (short) * gx_displayprop.cxwidth * gx_displayprop.cyheight );
If (framebuf = NULL)
Return false;
Clearscreen (framebuf, 0xff, 0xff, 0xff );
Gxopeninput ();
// Get default buttons for up/down etc.
Gx_keylist = gxgetdefaultkeys (gx_normalkeys );
After the initialization is complete, we need to prepare the game content. First, we initialize the greedy object. We create a snake head and its body for it. An event must be triggered to keep greedy people swimming. In this case, we use a timer to send messages at a ms interval, so that we can get 10 frames per second, which is enough to meet the performance of normal games.
To process the message sent by the timer, we call the run function.
Void run (hwnd)
{
If (1 = judgedeath (framebuf ))
{
Killtimer (hwnd, 1 );
Runvbrate (1000 );
MessageBox (hwnd, _ T ("Snake has died! ", _ T (" died ",
Mb_ OK | mb_iconinformation );
Sendmessage (hwnd, wm_paint, 0, 0 );
Invalidaterect (hwnd, null, true );
}
Changedirection ();
Sortall ();
Redrawsnake ();
}
Every time judgedeath determines whether the snake has died (any part of the snake overlaps), once the death condition is met, the timer is canceled to stop the snake swimming. Changedirection: determines whether the direction has changed. Here, I use the overlapping colors of two pixels to determine whether the two pixels are the same. If the colors are consistent, the overlap occurs.
Void get16pixel (unsigned short * buffer, int X, int y, int * r, int * g, int * B)
{
Unsigned short * pixeladd;
Int address = (x * gx_displayprop.cbxpitch> 1)
+ (Y * gx_displayprop.cbypitch> 1 );
Pixeladd = (buffer + address );
If (gx_displayprop.ffformat & kfdirect565)
{
Unsigned short pixelcol;
Pixelcol = (* pixeladd );
* R = (pixelcol & 0xf800)> 11;
* G = (pixelcol & 0x07e0)> 5;
* B = (pixelcol & 0x001f );
}
Else // 555.
{
Unsigned short pixelcol;
Pixelcol = (* pixeladd );
* R = (pixelcol & 0x7c00)> 11;
* G = (pixelcol & 0x03e0)> 5;
* B = (pixelcol & 0x001f );
}
}
Introduction to third-party development libraries
The Design of gapidraw is very similar to that of DirectDraw, which is easier to use and greatly optimizes the handheld devices. The following are some general functions of driectdraw and how to implement these functions in gapidraw.
Enable the Display Device
The Display memory of a computer is a memory area that contains image data. To directly write data to this area, both DirectDraw and gapidraw require you to create a designated interface called the main interface. Directly draw to the main interface to affect the visible content of the screen.
The first step to create the main interface is to enable the display and set a display mode. The following steps are the minimum steps for using DirectDraw to create the main interface.
DirectDraw
Lpdirectdraw lpdd;
Hresult ddrval;
// Create a primary Direct Draw object
Ddrval = directdrawcreate (null, & lpdd, null );
If (ddrval! = Dd_ OK)
{
Return (false );
}
// Set the cooperation level to allow direct draw to run in full screen
Ddrval = lpdd-> setcooperativelevel (hwnd, ddscl_exclusive | ddscl_fullscreen );
If (ddrval! = Dd_ OK)
{
Lpdd-> release ();
Return (false );
}
// Set the display mode to 320x240x16
Ddrval = lpdd-> setdisplaymode (320,240, 16 );
If (ddrval! = Dd_ OK)
{
Lpdd-> release ();
Return (false );
}
It is easier to use gapidraw. Because the interface is an object rather than a COM interface, you do not need to release it manually. In the following example, only one command is used to open the display device and set the default display mode.
Gapidraw
Cgapidisplay display;
Hresult gdrval;
// Enable the display in standard pocket pc240x320x16 Mode
Gdrval = display. opendisplay (hwnd, gdopendisplay_fullscreen );
If (gdrval! = Gd_ OK)
{
Return (false );
}
Retrieve the main interface and backend Buffer
To use Direct Draw, you must manually request to create a primary direcr draw object for the specified interface. The primary interface is mainly used for drawing directly on the display. There is only one interface in Direct Draw for dual-memory interface and display. This can be simply interpreted as a subclass defect in the com mode used in the past. The following example creates a main interface and uses direct draw to retrieve its back buffer.
DirectDraw
Lpdirectdrawsurface lpddsprimary; // DirectDraw Main Interface
Lpdirectdrawsurface lpddsback; // DirectDraw back buffer
Ddsurfacedesc ddsd;
Ddscaps;
Hresult ddrval;
Memset (& ddsd, 0, sizeof (ddsd ));
Ddsd. dwsize = sizeof (ddsd );
Ddsd. dwflags = ddsd_caps | ddsd_backbuffercount;
Ddsd. ddscaps. dwcaps = ddscaps_primarysurface | ddscaps_flip | ddscaps_complex;
Ddsd. dwbackbuffercount = 1;
// Create the main interface
Ddrval = lpdd-> createsurface (& ddsd, & lpddsprimary, null );
If (ddrval! = Dd_ OK)
{
Lpdd-> release ();
Return (false );
}
// Get the back buffer to obtain the backend Buffer
Ddscaps. dwcaps = ddscaps_backbuffer;
Ddrval = lpddsprimary-> getattachedsurface (& ddscaps, & lpddsback );
If (ddrval! = Dd_ OK)
{
Lpddsprimary-> release ();
Lpdd-> release ();
Return (false );
}
On the contrary, it is easier to use gapidraw. Once cgapisurface: opendisplay is called, The cgapidisplay object automatically becomes the main interface. Because cgapidisplay is a subclass of cgapisurface, all bitmap transmission and painting operations are available. To obtain the backend buffer from cgapidisplay, use the following code:
Gapidraw
Cgapisurface backbuffer; // gapidraw back buffer
Hresult gdrval;
// Obtain the backend Buffer
Gdrval = display. getbackbuffer (& backbuffer );
If (gdrval! = Gd_ OK)
{
Return (false );
}
Failed page
The DirectDraw interface is usually stored in Image Storage and can be overwritten at any time (when a user switches a program or starts another program using GDI ). This is because every operation on the interface may fail at any time, simply put, because the interface data is overwritten. Therefore, all operations using Direct Draw must check whether the interface fails each time, and then manually restore and recreate the interface from the failure. The following example illustrates this.
DirectDraw
Ddrval = lpddsback-> BLT (& rcrectdest, lpddsmysurf, & rcrectsrc, ddblt_wait, null );
If (ddrval = dderr_surfacelost)
{
// The interface is overwritten. Now you must manually restore and recreate all interfaces.
}
Ddrval = lpddsprimary-> flip (null, ddflip_wait );
If (ddrval = dderr_surfacelost)
{
// The interface is overwritten. Now you must manually restore and recreate all interfaces.
}
The Pocket PC does not use image storage. All interface data is stored in the ram physical memory. It is copied to the display area only when cgapidisplay: Flip is called. If the Pocket PC device accesses the buffer of the display area, the Pocket PC may move its back buffer position. So far, no device has been accused of doing this, but it is always the best design. You can use the following code to capture the missing backend buffer in gapidraw.
Gapidraw
// The interface cannot be lost in normal operations
Gdrval = backbuffer. BLT (& rcrectdest, & mysurf, & rcrectsrc, 0, null );
Gdrval = display. Flip ();
If (gdrval = gderr_backbufferlost)
{
// The display buffer is moved to obtain an updated backend buffer.
Display. getbackbuffer (& backbuffer );
}
Conclusion
The main difference between the gap between Direct Draw and gapidraw is mentioned above. Other features, such as bits, color values, and rectangular coordinates, are different. The gap draw also contains a huge extension feature, which is unavailable in Direct Draw, such as advanced bits impact, fast rotation, loading bitmap images from files or memory, drawing tools, conflicting masks, interface crossover, thread timers, bitmap font support, and more.