在前面几节中,测试代码主要是在main.cpp中编写的。但是,如果接下来还是在main.cpp中修改的话,要么添加静态函数需要传递的参数,要么需要各种全局变量,这两种方法都不是权宜之计。因此,应该有一个能统筹整个游戏开发,并且提供各种需要用到的功能的类,它就是Director(导演)类
导演类主要控制游戏流程,其功能包括:
1.游戏的绘制方面的设定,窗口的大小,FPS显示,帧率设置等。
2.切换当前的游戏场景,暂停或者恢复游戏场景的运行。
导演类的函数大致包括:
1.初始化函数,创建Window和Renderer等。
2.刷新函数,帧率控制和事件监听,一些对象内部状态的刷新。
3.功能函数,比如获取当前的窗口大小等。
4.场景切换函数(待实现)。负责从一个场景切换到另一个场景。
Director类是把main.cpp中的部分语句封装成自己的成员函数以及属性。
#ifndef __SDL_Director_H__
#define __SDL_Director_H__
#include <ctime>
#include "SDL.h"
#include "Object.h"
#include "Size.h"
NS_SDL_BEGIN
class Window;
class Renderer;
class Director : public Object
{
private:
static Director* _pInstance;
//窗口和渲染器
Window* _window;
Renderer* _renderer;
//是否正在运行
bool _running;
//每一帧花费的秒数
float _secondsPerFrame;
//是否显示状态
bool _displayStates;
//帧率控制
Uint32 _lastTick;
Uint32 _nextTick;
int _currentFrame;
int _lastFrame;
long _lastTime;
unsigned int _totalFrames;
private:
Director();
~Director();
public:
static Director* getInstance()
{
if (nullptr == _pInstance)
{
_pInstance = new Director();
}
return _pInstance;
}
static void purge();
//初始化函数,创建窗口和渲染器
bool init(const char* title,int x,int y,int width,int height,Uint32 flags);
//刷新
void update();
//是否正在运行
bool isRunning()const {return _running;}
void setRunning(bool running) {_running = running;}
//TODO:场景切换函数
//设置FPS
void setSecondsPerFrame(float secondsPerFrame) {_secondsPerFrame = secondsPerFrame;}
//状态
void setDisplayStates(bool displayStates) {_displayStates = displayStates;}
bool isDisplayStates()const {return _displayStates;}
//开启屏幕适配
int setResolutionScale();
//获取窗口
Window* getWindow() const {return _window;}
//获取渲染器
Renderer* getRenderer() const {return _renderer;}
//获取窗口可视大小
Size getVisibleSize() const;
private:
void handleScenes();
void updateStates();
};
NS_SDL_END
#endif
Director类为单例类,在程序运行期间有且仅有一个。
init为初始化函数,除了创建窗口和渲染器外以后还会进行一些必要的初始化。
bool Director::init(const char* title,int x,int y,int width,int height,Uint32 flags)
{
//初始化SDL
if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS)!=0)
{
printf("%s",SDL_GetError());
return false;
}
//创建窗口和渲染器
_window = Window::create(title,x,y,width,height,flags);
_renderer = Renderer::create(_window,-1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE
| SDL_RENDERER_PRESENTVSYNC);
//保持引用
_window->retain();
_renderer->retain();
//初始化随机数种子
srand((unsigned int)time(nullptr));
return true;
}
init函数中调用了初始化随机数种子语句,这个函数以后会在获取随机数的宏函数中用到,故在这里进行一次初始化。
void Director::update()
{
Uint32 currentTick = SDL_GetTicks();
//进行帧率控制
if(currentTick >= _nextTick)
{
++_totalFrames;
++_currentFrame;
//真实帧率
float dt = _secondsPerFrame;
_lastTick = currentTick;
_nextTick = _lastTick + (Uint32)(_secondsPerFrame * 1000);
//TODO:进行渲染和部分更新
}
this->handleScenes();
//更新FPS
this->updateStates(currentTick);
}
update函数的主要作用包括:
1.帧率控制。
2.渲染(待添加)。
3.对其他对象进行更新(待添加)。
4.监听事件(待添加)。
int Director::setResolutionScale()
{
Size originalSize = _window->getWindowSize();
Size realSize = _window->getRealWindowSize();
if(originalSize == realSize)
return 0;
Size scale = realSize / originalSize;
return _renderer->renderSetScale(scale.width,scale.height);
}
简单的屏幕适配,即根据创建的窗口大小和实际的窗口大小的比进行拉伸,仅在android等移动端才会发挥作用。
Size Director::getVisibleSize() const
{
return _window->getWindowSize();
}
//TODO
void Director::handleScenes()
{
}
void Director::updateStates(Uint32 currentTick)
{
//每过一秒就更新一下FPS
if(currentTick - _lastTime >= 1000)
{
//由于是从0开始加,所以得加一
_lastFrame = _currentFrame;
_currentFrame = 0;
_lastTime = currentTick;
//显示FPS,暂时输出在控制台
if(_displayStates)
{
printf("FPS:%d\n",_lastFrame);
}
}
}
updateStates函数的作用就是更新FPS的显示,当前显示在控制台(以后显示在屏幕的左上角)。
Director类创建完成,接下来就是main.cpp的修改。
#include <iostream>
#include "SDL_Engine.h"
#include "SDL.h"
#include "vld.h"
USING_NS_SDL;
using namespace std;
int main(int argc,char** argv)
{
//窗口创建成功
if(Director::getInstance()->init("SDL_Engine Test",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,800,480,SDL_WINDOW_SHOWN))
{
Director::getInstance()->setDisplayStates(true);
Director::getInstance()->setSecondsPerFrame(1/60.f);
Director::getInstance()->setResolutionScale();
while(Director::getInstance()->isRunning())
{
Director::getInstance()->update();
PoolManager::getInstance()->getCurReleasePool()->clear();
}
//系统清除
Director::purge();
//释放内存池
PoolManager::getInstance()->purge();
}
return 0;
}
在main函数中,先调用Director的init初始化函数,创建了窗口和渲染器,然后显示FPS,并设置FPS为60帧,并开启了屏幕适配(在android等移动端有效),之后便是一直的循环,每一帧都进行更新和释放池的刷新。在程序退出时,回收内存。另外需要注意的是,main函数中的内容在以后的开发中基本是固定的。
另外,由于本节没有监听事件,所以SDL窗口是无法移动或者是关闭的,这个在添加了事件监听后会消除此现象(在第三四节中有提到)。
本节代码:链接:https://pan.baidu.com/s/13CIQvwjjUHpKcG9B4omRLA 密码:k7hf