最近在用Ogre+CEGUI+OIS做游戏。在开发的过程出现了种种问题,现将问题一一整理,并将解决方案释出,供同好参阅。
OIS默认鼠标占用模式是鼠标独占使用,表现为鼠标指针无法移除当前窗口。那么如果想把鼠标指针移除窗体怎么解决呢?让OIS使用鼠标协作模式啊!代码如下:
//创建设备
OIS::ParamList pl;
size_t windowHnd = 0;
std::ostringstream windowHndStr;
m_Window->getCustomAttribute(Ogre::String(”WINDOW”), &windowHnd);
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string(”WINDOW”), windowHndStr.str()));
pl.insert(std::make_pair(std::string(”w32_mouse”), std::string(”DISCL_FOREGROUND”)));
pl.insert(std::make_pair(std::string(”w32_mouse”), std::string(”DISCL_NONEXCLUSIVE”)));
m_InputManager = OIS::InputManager::createInputSystem( pl );
这下好了,鼠标可以移除当前窗体了,突然发现没对,怎么有两个鼠标指针?好嘛,我就在当前窗口隐藏掉系统的指针!代码如下:
ShowCursor(0); //隐藏系统鼠标指针
这回应该没问题了吧~~~啦啦啦,还是没对哦!鼠标定位不准确~~~~~~~~,行!那就在mouse::MouseMoved事件中把 Moved(e.state.X.rel, e.state.Y.rel)替换成Postion(e.state.X.abs, e.state.Y.abs)
在编译运行,矣~~~~~~咋个指针在一个很小范围内移动呢?哦,原来OIS默认宽和高是50px啊,来设置成RenderWindow的宽高即可!代码如下:
_mouse->getMouseState().width=_render_window->getWidth();
_mouse->getMouseState().height=_render_window->getHeight();
嘿嘿,这下完美了!oh,my god~~~~~~~~为什么这样对我?鼠标都移除窗体了,为什么窗体内的那个指针还要动,那我就在MouseMoved中做边界检查,对了为了防止误点,还要在mousePressed和 mouseReleased做边界检查,代码就不啰嗦了!~~~=
至此OGRE+OIS+CEGUI鼠标非独占使用,完美解决!
---------------------------------------------------------------------------------
最近在弄SuperRacing的时候,想把游戏鼠标换成非独占模式。全屏下,独占模式还不错,但在窗口模式下就显得不习惯了。鼠标只能在窗口里面移动,不能移动到窗口外的地方,在操作上显然不太友好。于是,我google了一下"ois 非独占",看到了很多完美的解决办法。下面就说说我自己的使用方法吧!
隐藏系统鼠标:创建窗口后,就隐藏系统鼠标,不然会出现二个鼠标的现象。::ShowCursor(FALSE);
创建OIS输入设备时指定非独占:InputManager::createInputSystem()创建设备时,参数实际上是一个std::multimap<std::string,std::string>类型的变量,通过它来传入参数值。 如: ParamList pl;
std::ostringstream windowHndStr;
windowHndStr <<(size_t)hwnd; // 窗口句柄
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); // 传入窗口句柄
pl.insert(std::make_pair(std::string("w32_mouse"), "DISCL_FOREGROUND"));
pl.insert(std::make_pair(std::string("w32_mouse"), "DISCL_NONEXCLUSIVE")); // 设置鼠标为协作模式
// 创建输入设备
mInputManager = InputManager::createInputSystem( pl );
mKeyboard = static_cast<Keyboard*>(mInputManager->createInputObject( OISKeyboard, true));
mMouse = static_cast<Mouse*>(mInputManager->createInputObject( OISMouse,true ));
// 设置回调
mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);
// 设置鼠标移动范围,OIS默认范围为50*50
mMouse->getMouseState().width =windowWidth;
mMouse->getMouseState().height = windowHeight;
注入CEGUI鼠标事件:先进行边界判断,当鼠标移出窗体外的时候,OIS会把MouseEvent.state.X.abs和MouseEvent.state.Y.abs设置成最大或最小值。比如渲染窗口大小为800*600,那么当鼠标移到窗口左边时,MouseEvent.state.X.abs会等于0而不会出现负数;移到窗口右边的时候,MouseEvent.state.Y.abs的值也不会超过800。所以我们可以如下边界判断:
if( mouseEvent.state.X.abs>0 && mouseEvent.state.X.abs<windowWidth && mouseEvent.state.Y.abs>0 && mouseEvent.state.Y.abs<windowHeight )
CEGUI::System::getSingleton().injectMousePosition( mouseEvent.state.X.abs, mouseEvent.state.Y.abs );
适应窗口大小变化:如果程序可以动态改变窗口大小(更多可以参考http://hghhe.blog.163.com/blog/static /3237756820099163237160/edit/),那么鼠标大小也要修改重新适应窗口。当窗口大小变化后,OIS只需重新调用 mMouse->getMouseState().width =windowWidth; mMouse->getMouseState().height = windowHeight;来重新设置鼠标移动范围。当然你可能会发现游戏窗口内的鼠标位置和系统鼠标位置不一致,那就要重新设置CEGUI鼠标显示的范围了,如:mGUIRenderer->setDisplaySize(1024,768)。
全屏与窗口切换:当游戏全屏的时候,输入使用独占模式更好一些,因为鼠标就不会移出游戏界面了(比如向上移碰到最上面的QQ)。其实在全屏与窗口的切换时,我们可以把OIS设备释放再重新创建。在窗口下使用非独占模式,而在全屏下使用独占模式。