Upload
ethel-lindsey
View
223
Download
2
Embed Size (px)
Citation preview
Game Programming 04The Game Loop and Real-time
Simulation
2010 년 2 학기디지털콘텐츠전공
Time in Game
• Games:– Real-time, dynamic, interactive computer
simulation– Time plays an important role!– Different types of time in a game
• Real time• Game time• local timeline of an animation
Rendering Loop
• Real-time 3D Graphics is different from OS’s GUI– Redraw in Windows PC/Mac OS:
• Mostly static• Small portion of the screen is changing• Rectangle invalidation
– Redraw in a Game:• Entire contents of the screen is changing• Like a movie with a series of still images in rapid
succession• Succession requires a LOOP – the rendering loop
Rendering Loop
• Typical structure for a rendering loop:
while(!quit){
// Update camera transform based on // user input, or predefined scenarioupdateCamera();
// Update positions, orientations and// other visual states of the elementsupdateSceneElements();
// Render a still frame into the “back buffer”
renderScene();
// Swap the back buffer with the front bufferswapBuffers();
}
while(!quit){
// Update camera transform based on // user input, or predefined scenarioupdateCamera();
// Update positions, orientations and// other visual states of the elementsupdateSceneElements();
// Render a still frame into the “back buffer”
renderScene();
// Swap the back buffer with the front bufferswapBuffers();
}
Game Loop
• Game consists of many subsystems:– Device I/O– Rendering– Animation– Collision detection and resolution– Rigid body dynamic simulation (optional)– Multiplayer networking– Audio
• Need to be update periodically – Game Loop– Update rates can be different
• Rendering/animation: 30~60 Hz• Dynamic simulation: 120 Hz• AI: 2 Hz (twice per second)
Game Loop – A Simple Example• Pong (Atari arcade game)– A Legendary Game:
http://www.pong-story.com/arcade.htm – Simple but exciting!
http://www.youtube.com/watch?v=LPkUvfL8T1I
Pong Game Loopvoid main() // pong{
initGame();while(true) // Game Loop{
readInterfaceDevices();if(quitButtonPressed())
break;movePaddles();moveBall();collideAndBounceBall();if(ballImpactedSide(LEFT_PLAYER){
incrementScore(RIGHT_PLAYER);resetBall();
}if(ballImpactedSide(RIGHT_PLAYER){
incrementScore(LEFT_PLAYER);resetBall();
}renderPlayfield();
}}
void main() // pong{
initGame();while(true) // Game Loop{
readInterfaceDevices();if(quitButtonPressed())
break;movePaddles();moveBall();collideAndBounceBall();if(ballImpactedSide(LEFT_PLAYER){
incrementScore(RIGHT_PLAYER);resetBall();
}if(ballImpactedSide(RIGHT_PLAYER){
incrementScore(LEFT_PLAYER);resetBall();
}renderPlayfield();
}}
Game loop architectural styles1. Windows Message Pumps
2. Callback-Driven Frameworks
3. Even-based Updating
Windows Message Pumps
• Message Pump:– A routine for handling windows messages
• Games in a Windows OS must obey windows’ rule• Many messages from the OS cannot be ignored
// Message Pump – Typical Windows Routine
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); DispatchMessage(&msg);
}
// Message Pump – Typical Windows Routine
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); DispatchMessage(&msg);
}
Windows Message Pumps
• Message Pump for a game:– Modify the windows message pump with the
game loop// Message Pump for a gamewhile (true){
// Service all pending windows messageswhile (PeekMessage(&msg, NULL, 0, 0)>0) {
TranslateMessage(&msg); DispatchMessage(&msg);
}
// If no more windows messages, // run one iteration of the game loopRunOneIterationOfGameLoop();
}
// Message Pump for a gamewhile (true){
// Service all pending windows messageswhile (PeekMessage(&msg, NULL, 0, 0)>0) {
TranslateMessage(&msg); DispatchMessage(&msg);
}
// If no more windows messages, // run one iteration of the game loopRunOneIterationOfGameLoop();
}
Call-back Driven Framework
• Framework:– A partially-constructed application
• Encapsulating knowledge of how to use the low-level libraries
• Providing functions for the game flow • But functions are mostly empty
– Need to be completed by a programmer• Fill-in the missing details• Overide call-back functions
Call-back Driven Framework
• Example from Ogre3D’s game loop
while (true){
for (each frameListener){
frameListener.frameStarted();}
renderCurrentScene();
for (each frameListener){
frameListener.frameEnded();}
}
while (true){
for (each frameListener){
frameListener.frameStarted();}
renderCurrentScene();
for (each frameListener){
frameListener.frameEnded();}
}
Call-back Driven Framework
• Using Ogre’s FrameListenerClass GameFrameListener : public Ogre::FrameListener{public:
virtual void frameStarted(const FrameEvent &event){
// Do things happening before renderingpollJoypad(event);updatePlayerControls(events);updateDynamicSimulation(events);resolveCollisions(events);updateCamera(events);// etc.
}virtual void frameEnded(const FrameEvent &event){
// Do things after renderingdrawHud(events);
}}
Class GameFrameListener : public Ogre::FrameListener{public:
virtual void frameStarted(const FrameEvent &event){
// Do things happening before renderingpollJoypad(event);updatePlayerControls(events);updateDynamicSimulation(events);resolveCollisions(events);updateCamera(events);// etc.
}virtual void frameEnded(const FrameEvent &event){
// Do things after renderingdrawHud(events);
}}
Event-Driven Updating
• Event:– Any interesting change in the state of the
game– Examples:
• The player pressing a keyboard• The explosion going off• An enemy character spotting the player
Event-Driven Updating
• Event System– Similar to windows GUI– A subsystem of a game engine
• Register events of interests• Respond to the events when they occur
– Periodic services can be implemented by an event• Example: Posting a event for every 1/30 sec.• Using the event que for posting a event for the future
MEASURING AND DEALING WITH TIME
Timelines
• Real Time– Actual time measured directly via the CPU
clock
• Game Time– Times measured or used in the game– Does not need to be coincide with the real time
• Slow/fast motion in the game • Temporarily pausing the game• When debugging, slow-down helps to figure out.
Timelines
• Local and Global timelines– Each animation clip or audio clip may have a
different timeline• Origin (t=0) defined to the start of the clip• Easy to speed up/down when playback• Easy to play backward
Playing a clipPlaying a clip Speed upSpeed up Play backwardPlay backward
Frame Rate and Time Deltas
• Frame Rate: f– How rapidly the sequence of still images
changes– Hertz (Hz) : number of cycles for second– Frames per Second (FPS) :
• Used in games and film. • Same with Hz • 30/60 in North America or Japan (NTSC)• 50 in Europe (PAL)
– Can be varying time to time!
• Time Delta: Δt– The amount of time between frames:
• Also known as Frame time, delta time• Inverse of the frame rate: 1/f• 30 FPS = 1/30 sec = 33.3 ms
From Frame Rate to Speed
• Speed: v– Meters per second (or pixels per second)
• Make a spaceship move:– Change in position: Δx = v Δt
– With a current position x1, at next frame: x2 = x1 + v Δt
– Since the movement depends on Δt , how to determine it?
Old-days CPU-dependent Games• CPU-Dependent:– Do not use any Δt ! – Instead, specify the speed by the meters per frame
– With a good PC, will runs fast– With a bad PC, will runs slow– Provide a “Turbo” button to speed up in old-days
Updating based on Elapsed Time– To be CPU-independent: Measuring Δt
• Read CPU time twice: • Once at the beginning and once at the end of the frame• Most game do this!
– Is it accurate?• We are measuring the past Δt as an upcomming Δt
• “Past performance is not a guarantee of future results”
• Sudden drop in the frame rate: Frame-rate spike
•
Using a Running Average
• Averaging the frame-time over a few frames– Reduce the influence of a sudden change in
frame rate– Can adapt smoothly with a varying framerate
Governing the Frame Rate
• Frame Rate Governing:– Guarantee every frame’s duration to be 33.3
ms or 16.6 ms– Sleep if ends earlier otherwise “takes our lumps”
Measuring Real Time
• Querying system time:– The number of seconds since midnight, Jan 1, 1970– Not enough resolution
• High-Resolution Timer:– Count the number of CPU cycles since the last
power on/reset– For example:
• 3GHz processor increments 3 billion times per second• Resolution: 1/3 billion = 3.33 x 10-10 second =0.333 ns• Stored with 64 bit integer: 195 years of duration to
record
time();time();
QueryPerformanceCounter(); // read the counter (64bit integer)QueryPerformanceFrequency(); // cycles per secondQueryPerformanceCounter(); // read the counter (64bit integer)QueryPerformanceFrequency(); // cycles per second
Time to Back to OGRE3D
Basic of OGRE• Scene Manager:
– Managing everything appearing in the scene– Cameras, Objects, Planes, billboards, lights...and so
on
• 3 kinds of Scene Managers– Octree(8 진 트리 ) Scene Manager
• 기본 장면 관리자• 대부분의 장면에 사용 가능
– BSP(Binary Surface Partition) Scene Manager• 건물 내부와 같이 벽과 복도 등으로 구성된 장면에 최적화된
성능 .
– Terrain Scene Manager• 정적 지형을 가지는 비교적 소규모의 장면에 적합• 고해상도의 지형에 적합 mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC); mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
Basic of OGRE
• Entity:– Objects to be rendered on the scene – 3D meshes: Character, robot, fish, terrain and
so on. – Cameras, lights, billboards are not entities.
3D Mesh
Basic of OGRE
• Scene Node:– Representing translation, rotation and scale
(transformation)– Not an actual object– An(or multiple) entity can be attached to.
NodeNode
Entity1
Entity1
Entity2
Entity2
Scene Manager, node and entity
rootroot
Node1Node1 Node2Node2
Entity1
Entity1
Entity2
Entity2
Entity3
Entity3
Node3Node3
Entity4
Entity4
Entity5
Entity5
Node4Node4
Entity6
Entity6
Hierarchical Modeling
• A hierarchical model is created by nesting the descriptions of subparts into one another to form a tree organization
Revisiting the code
void TutorialApplication::createScene(void){ // create your scene here :)
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); headNode->attachObject(ogreHead); // Set ambient light mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5)); // Create a light Ogre::Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50);}
void TutorialApplication::createScene(void){ // create your scene here :)
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); headNode->attachObject(ogreHead); // Set ambient light mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5)); // Create a light Ogre::Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50);}
Adding One More Node
void TutorialApplication::createScene(void){ // create your scene here :)
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); headNode->attachObject(ogreHead); Ogre::Entity* ogreHead2 = mSceneMgr->createEntity( "Head2", "ogrehead.mesh" ); Ogre::SceneNode* headNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode ( "HeadNode2", Ogre::Vector3( 100, 0, 0 ) ); headNode2->attachObject( ogreHead2 );
// Set ambient light mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5)); // Create a light Ogre::Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50);}
void TutorialApplication::createScene(void){ // create your scene here :)
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); headNode->attachObject(ogreHead); Ogre::Entity* ogreHead2 = mSceneMgr->createEntity( "Head2", "ogrehead.mesh" ); Ogre::SceneNode* headNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode ( "HeadNode2", Ogre::Vector3( 100, 0, 0 ) ); headNode2->attachObject( ogreHead2 );
// Set ambient light mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5)); // Create a light Ogre::Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50);}
Node Transformation
• Translate:– SceneNode::translate ( Ogre::Vector3(..) );
• Rotate:– SceneNode::yaw(Degree(..));– SceneNode::pitch ( );– SceneNode::roll ( );
• Scale– SceneNode::scale ( x, y, z );
Cool ninja without hassle
• Replace the CreateScene function with the one in our homepage!
정리• OGRE 3D 의 rendering process:– 시스템 초기화
• Root 개체 생성• Configuration 설정• Render Window 생성• Resource 위치 설정• Frame Listener 생성
– 씬 생성• Scene Manager 생성• Camera 및 viewport 생성• Light 생성• Entity 생성• Scene node 생성
– 렌더링
Frame Listener
• 한 장면이 화면에 렌더링되기 직전 /직후에 해야 할 일을 정의하고 있는 객체
• 추가 작업을 위해서는 frame listener 를 추가 생성하고 , 이를 root 에 등록시켜 사용
• 여러 개의 frame listener 가 존재할 수 있다 .
Frame Listener 의 맴버함수
frameStarted Called just before a frame is rendered.
frameRenderingQueued
Called after all render targets have had their rendering commands issued, but beforethe render windows have been asked to flip their buffers over
frameEnded Called just after a frame has been rendered.
virtual bool frameStarted(const FrameEvent& evt); virtual bool frameRenderingQueued(const FrameEvent& evt); virtual bool frameEnded(const FrameEvent& evt)
virtual bool frameStarted(const FrameEvent& evt); virtual bool frameRenderingQueued(const FrameEvent& evt); virtual bool frameEnded(const FrameEvent& evt)
FrameEvent structure: Real timeSinceLastEvent, Real timeSinceLastFrame 값을 기억하는 구조체
Frame Listener 의 호출• Root 객체의 rendering loop 내에서 호출
bool Root::renderOneFrame(void){ if(!_fireFrameStarted()) return false; if (!_updateAllRenderTargets()) return false; return _fireFrameEnded();}
bool Root::renderOneFrame(void){ if(!_fireFrameStarted()) return false; if (!_updateAllRenderTargets()) return false; return _fireFrameEnded();}
Ogre Engine 의 Rendering Loop• Root::startRendering() 함수에서 이뤄짐• Redering Loop 실행
– 매 프레임 마다 renderOneFrame 실행 :
• Loop 의 중단– FrameStarted, frameRenderingQueued, FrameEnded 에서
하나라도 false 를 return 할 경우
1. Frame Listener 들의 FrameStarted() 함수 호출1. Frame Listener 들의 FrameStarted() 함수 호출
3. Frame Listener 들의 FrameEnded() 함수 호출3. Frame Listener 들의 FrameEnded() 함수 호출
Frame Listener 들의 frameRenderingQueued() 함수 호출Frame Listener 들의 frameRenderingQueued() 함수 호출
2. 한 프레임 렌더링2. 한 프레임 렌더링
Input Handling
• Types of input handling– Unbuffered Input
• Testing states of Mouse/Keyboard every frame• Do action if necessary
– Buffered Input• Event(message)-driven• Event is queued into a buffer• Call message pump routines per frame
Ninja 움직이기• Add two functions in Application class:
class OgreApp : public BaseApplication{public: BasicTutorial4(void); virtual ~BasicTutorial4(void);protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);private: bool processUnbufferedInput(const Ogre::FrameEvent& evt);}
class OgreApp : public BaseApplication{public: BasicTutorial4(void); virtual ~BasicTutorial4(void);protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);private: bool processUnbufferedInput(const Ogre::FrameEvent& evt);}
Ninja 움직이기• Add two functions in Application class:
class OgreApp : public BaseApplication{public: BasicTutorial4(void); virtual ~BasicTutorial4(void);protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);private: bool processUnbufferedInput(const Ogre::FrameEvent& evt);}
class OgreApp : public BaseApplication{public: BasicTutorial4(void); virtual ~BasicTutorial4(void);protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);private: bool processUnbufferedInput(const Ogre::FrameEvent& evt);}
CreateScene 수정• Ninja 를 포함하고 light 를 세팅
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.25, 0.25, 0.25));
Ogre::Entity* ninjaEntity = mSceneMgr->createEntity("Ninja", "ninja.mesh");Ogre::SceneNode *node = mSceneMgr->getRootSceneNode()
->createChildSceneNode("NinjaNode");node->attachObject(ninjaEntity);
Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");pointLight->setType(Ogre::Light::LT_POINT);pointLight->setPosition(Ogre::Vector3(250, 150, 250));pointLight->setDiffuseColour(Ogre::ColourValue::White);pointLight->setSpecularColour(Ogre::ColourValue::White);
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.25, 0.25, 0.25));
Ogre::Entity* ninjaEntity = mSceneMgr->createEntity("Ninja", "ninja.mesh");Ogre::SceneNode *node = mSceneMgr->getRootSceneNode()
->createChildSceneNode("NinjaNode");node->attachObject(ninjaEntity);
Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");pointLight->setType(Ogre::Light::LT_POINT);pointLight->setPosition(Ogre::Vector3(250, 150, 250));pointLight->setDiffuseColour(Ogre::ColourValue::White);pointLight->setSpecularColour(Ogre::ColourValue::White);
Frame Listner 수정• frameRenderingQueued 함수 override
bool OgreApp::frameRenderingQueued(const Ogre::FrameEvent& evt){ bool ret = BaseApplication::frameRenderingQueued(evt); if(!processUnbufferedInput(evt)) return false; return ret;}
bool OgreApp::frameRenderingQueued(const Ogre::FrameEvent& evt){ bool ret = BaseApplication::frameRenderingQueued(evt); if(!processUnbufferedInput(evt)) return false; return ret;}
Processing Input
bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt){ static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant
bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt){ static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant
Processing Input
• UnbufferedInput 구현– Mouse 와 keyboard 의 현재 상태를 얻어옴
(BaseApplication::frameRenderingQueued 에서 수행 )
• 다음과 같이 사용할 준비를 한다 . bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt){ static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant
bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt){ static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant
mMouse->capture(); mKeyboard->capture();mMouse->capture(); mKeyboard->capture();
Toggle 기능 구현• 현재 mouse 와 마지막 mouse 상태의 비교를
통해 구현
bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left);
if (currMouse && ! mMouseDown){ Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible());}
mMouseDown = currMouse;
bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left);
if (currMouse && ! mMouseDown){ Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible());}
mMouseDown = currMouse;
Delayed Toggle 구현• 눌린 시간을 기억하여 어느 일정시간이상
눌렸으면 실행
mToggle -= evt.timeSinceLastFrame;
if ((mToggle < 0.0f ) && mKeyboard->isKeyDown(OIS::KC_1)){ mToggle = 0.5; Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible());}
mToggle -= evt.timeSinceLastFrame;
if ((mToggle < 0.0f ) && mKeyboard->isKeyDown(OIS::KC_1)){ mToggle = 0.5; Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible());}
Keyboard 입력 구현• 키보드의 상태를 찾아내어 구현
Ogre::Vector3 transVector = Ogre::Vector3::ZERO;
if (mKeyboard->isKeyDown(OIS::KC_I)) // ForwardtransVector.z -= mMove;
if (mKeyboard->isKeyDown(OIS::KC_K)) // BackwardtransVector.z += mMove;
if (mKeyboard->isKeyDown(OIS::KC_U)) // UptransVector.y += mMove;
if (mKeyboard->isKeyDown(OIS::KC_O)) // DowntransVector.y -= mMove;
mSceneMgr->getSceneNode("NinjaNode")->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
Ogre::Vector3 transVector = Ogre::Vector3::ZERO;
if (mKeyboard->isKeyDown(OIS::KC_I)) // ForwardtransVector.z -= mMove;
if (mKeyboard->isKeyDown(OIS::KC_K)) // BackwardtransVector.z += mMove;
if (mKeyboard->isKeyDown(OIS::KC_U)) // UptransVector.y += mMove;
if (mKeyboard->isKeyDown(OIS::KC_O)) // DowntransVector.y -= mMove;
mSceneMgr->getSceneNode("NinjaNode")->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
Rotate 구현• SHIFT 키와 조합하여 구현
if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe{ if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw left mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(mRotate * 5)); } else { transVector.x -= mMove; // Strafe left }}if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe{ if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw right mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(-mRotate * 5)); } else { transVector.x += mMove; // Strafe right }}
if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe{ if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw left mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(mRotate * 5)); } else { transVector.x -= mMove; // Strafe left }}if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe{ if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw right mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(-mRotate * 5)); } else { transVector.x += mMove; // Strafe right }}