首先还是就我个人的理解,讲讲游戏引擎的处理流程。
其实游戏逻辑简单化就是一个死循环,如下:
- bool game_is_running = true;
- while( game_is_running ) {
- update_game();
- display_game();
- }
我们所看到的游戏画面,游戏音乐,以及一些触控,输入等。在逻辑上就是这么一个死循环。这个循环一直在跑,期间会处理一些列的事件,简化之就是上面的两个函数。
cocos2d-x引擎也是如此,所有的逻辑都是在这个主循环下实现的。下面看看cocos2dx在各平台上的主循环实现。
1.Win
看它的main.cpp
- #include "main.h"
- #include "../Classes/AppDelegate.h"
- #include "CCEGLView.h"
- USING_NS_CC;
- int APIENTRY _tWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- {
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
- // create the application instance
- AppDelegate app;
- CCEGLView* eglView = CCEGLView::sharedOpenGLView();
- eglView->setFrameSize(2048, 1536);
- // The resolution of ipad3 is very large. In general, PC's resolution is smaller than it.
- // So we need to invoke 'setFrameZoomFactor'(only valid on desktop(win32, mac, linux)) to make the window smaller.
- eglView->setFrameZoomFactor(0.4f);
- return CCApplication::sharedApplication()->run();// 注意这里
- }
前面都不要关心,只是用来传递OpenGL窗口的,关键是最后一句,CCApplication::sharedApplication()->run()。看这个run函数:
- int CCApplication::run()
- {
- PVRFrameEnableControlWindow(false);
- // Main message loop:
- MSG msg;
- LARGE_INTEGER nFreq;
- LARGE_INTEGER nLast;
- LARGE_INTEGER nNow;
- QueryPerformanceFrequency(&nFreq);
- QueryPerformanceCounter(&nLast);
- // Initialize instance and cocos2d.
- if (!applicationDidFinishLaunching())
- {
- return 0;
- }
- CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
- pMainWnd->centerWindow();
- ShowWindow(pMainWnd->getHWnd(), SW_SHOW);
- while (1)// 注意这里,主循环来了
- {
- if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- {
- // Get current time tick.
- QueryPerformanceCounter(&nNow);
- // If it's the time to draw next frame, draw it, else sleep a while.
- if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
- {
- nLast.QuadPart = nNow.QuadPart;
- CCDirector::sharedDirector()->mainLoop(); //看看这是神马
- }
- else
- {
- Sleep(0);
- }
- continue;
- }
- if (WM_QUIT == msg.message)
- {
- // Quit message loop.
- break;
- }
- // Deal with windows message.
- if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return (int) msg.wParam;
- }
不熟悉windows的童鞋估计都知道windows是消息驱动的。这个死循环就是用来处理windows的消息循环的,在其中处理了FPS逻辑,消息分发等。注意看其中红色标标注的
- CCDirector::sharedDirector()->mainLoop();
这是神马东西啊!这个就是cocos2d-x的主循环了,由导演负责维护。从此就进入了cocos2d-x的世界,跟windows没有一毛钱关系了。
2.iOs
ios上面和Android上类似。看AppController.mm
- #import <UIKit/UIKit.h>
- #import "AppController.h"
- #import "cocos2d.h"
- #import "EAGLView.h"
- #import "AppDelegate.h"
- #import "RootViewController.h"
- @implementation AppController
- @synthesize window;
- @synthesize viewController;
- #pragma mark -
- #pragma mark Application lifecycle
- // cocos2d application instance
- static AppDelegate s_sharedApplication;
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- // Override point for customization after application launch.
- // Add the view controller's view to the window and display.
- window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
- EAGLView *__glView = [EAGLView viewWithFrame: [window bounds]
- pixelFormat: kEAGLColorFormatRGBA8
- depthFormat: GL_DEPTH_COMPONENT16
- preserveBackbuffer: NO
- sharegroup: nil
- multiSampling: NO
- numberOfSamples:0 ];
- // Use RootViewController manage EAGLView
- viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
- viewController.wantsFullScreenLayout = YES;
- viewController.view = __glView;
- // Set RootViewController to window
- if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
- {
- // warning: addSubView doesn't work on iOS6
- [window addSubview: viewController.view];
- }
- else
- {
- // use this method on ios6
- [window setRootViewController:viewController];
- }
- [window makeKeyAndVisible];
- [[UIApplication sharedApplication] setStatusBarHidden: YES];
- <span style="color:#ff0000;">cocos2d::CCApplication::sharedApplication()->run();</span>//<span style="color:#ff0000;">看这里</span>
- return YES;
- }
- - (void)applicationWillResignActive:(UIApplication *)application {
- /*
- Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
- Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
- */
- cocos2d::CCDirector::sharedDirector()->pause();
- }
- - (void)applicationDidBecomeActive:(UIApplication *)application {
- /*
- Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
- */
- cocos2d::CCDirector::sharedDirector()->resume();
- }
- - (void)applicationDidEnterBackground:(UIApplication *)application {
- /*
- Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
- If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
- */
- cocos2d::CCApplication::sharedApplication()->applicationDidEnterBackground();
- }
- - (void)applicationWillEnterForeground:(UIApplication *)application {
- /*
- Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
- */
- cocos2d::CCApplication::sharedApplication()->applicationWillEnterForeground();
- }
- - (void)applicationWillTerminate:(UIApplication *)application {
- /*
- Called when the application is about to terminate.
- See also applicationDidEnterBackground:.
- */
- }
- #pragma mark -
- #pragma mark Memory management
- - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
- /*
- Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
- */
- cocos2d::CCDirector::sharedDirector()->purgeCachedData();
- }
- - (void)dealloc {
- [super dealloc];
- }
- @end
直接看标注,跟进run()
- <span style="font-size:18px;">int CCApplication::run()
- {
- if (applicationDidFinishLaunching())
- {
- <span style="color:#ff0000;">[[CCDirectorCaller sharedDirectorCaller] startMainLoop]</span>;
- }
- return 0;
- }</span>
再跟标注的startMainLoop()
- -(void) startMainLoop
- {
- // CCDirector::setAnimationInterval() is called, we should invalidate it first
- [displayLink invalidate];
- displayLink = nil;
- NSLog(@"run loop !");
- displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];//看这里
- [displayLink setFrameInterval: self.interval];
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- }
好了,看红色标注。这个貌似循环不起来啊,呵呵。
仔细看他加载的这个类CADisplayLink,就是这个东西循环起来的。这其实就是个定时器,默认每秒运行60次,其有个属性可以设置FPS。看后面有个回调函数doCaller,跟进
- -(void) doCaller: (id) sender
- {
- cocos2d::CCDirector::sharedDirector()->mainLoop();//看这里
- }
好了,终于又看到导演了。导演很忙,又开始主循环了。
一旦进入主循环,游戏就开始我们自己设计的游戏逻辑。