上网搜一搜便可以发现有关Yake的资料可谓少之有少,几乎可以算是没有,因为能搜到的基本上都会超链接到官网上去,无奈只好看着官网上那几个过期的小DEMO学习.不知道为会连个SDK都不给.
原始的资料来至http://www.yake.org/wiki/doku.php/tutorials:beginners:yap_1._the_basics
我只是稍加翻译和改动.
1.引言
这个教程的第一个版本是由Regress(不知道是谁:))制作的,我只是将原始的代码重写一遍,让它更容易理解一些.我不是一个Yake或C++的高手,所以如果有什么地方错了,请毫不犹豫的纠正我.事实上我写这个教程是作为我研究Yake的一个练习,后来我想共享它是一个不错的想法.
2.Basic application(不知道怎么翻是对的......)
在编写RAF应用程序之前我们需要一些有用的头文件
#include "yake\base\yake.h"
#include "yake\audio\yakeAudio.h"
#include "yake\input\yakeInput.h"
//#include "yapp\base\yapp.h" 这行注释掉是因为在yake新的版本中没有yapp这个文件夹了
//#include "yapp\raf\yakeRaf.h" 在新版本中换成了#include "yake\raf\yakeRaf.h"
#include "yake\raf\yakeRaf.h"
#if YAKE_PLATFORM == PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
如果是自己在别的地方建的工程,则要引入
#pragma comment(lib, "yake_base_d.lib")
#pragma comment(lib, "yake_scripting_d.lib")
#pragma comment(lib, "yake_raf_d.lib")
#pragma comment(lib, "yake_input_d.lib")
#pragma comment(lib, "yake_graphics_d.lib")
#pragma comment(lib, "yake_graphicsOgre_d.lib")
#pragma comment(lib, "yake_res_d.lib")
#pragma comment(lib, "yake_audio_d.lib")
#pragma comment(lib, "yake_ent_d.lib")
#pragma comment(lib, "yake_data_d.lib")
#pragma comment(lib, "yake_loader_d.lib")
//#pragma comment(lib, "yake_model_d.lib")
//#pragma comment(lib, "yake_gui2_d.lib")
//#pragma comment(lib, "yake_audioFMOD_d.lib")
//#pragma comment(lib, "yake_scriptingLua_d.lib")
//#pragma comment(lib, "yake_task_d.lib")
#pragma comment(lib, "yake_physics_d.lib")
//#pragma comment(lib, "yake_physicsODE_d.lib")
//#pragma comment(lib, "yake_vehicle_d.lib")
好了,那么在我们的RAF应用程序中要做的第一件事就是...应用程序它本身(似乎是美国人的幽默...),由于RAF,这很简单,我们只要从RAF框架中继承ExampleApplication类,因为它是一个很简单的应用程序,所以我们只使用一种状态.
//Very basic application with only one state.
class TheApp : public raf::ExampleApplication<TheConfiguration>
{
public:
TheApp(void);
protected:
virtual raf::MainState* createMainState()
{
return new YapMainState(*this);
}
};
ExampleApplication 为了包装raf base application的配置使用了一个模板.这个配置是一个类,它可以被用来告诉yake我们调用了哪些模块,他们基于哪些插件.
//Configuration struct, storing the module used for our application.
struct TheConfiguration : public raf::ApplicationConfiguration
{
virtual StringVector getLibraries()
{ return MakeStringVector() << "graphicsOgre" << "inputOgre"; }
virtual StringVector getInputSystems()
{ return MakeStringVector() << "ogre"; }
virtual StringVector getGraphicsSystems()
{ return MakeStringVector() << "ogre3d"; }
};
在这节教程中我们将用到Ogre3d来作渲染,用到Ogreinput来作为键盘和鼠标的输入.在下一课中我们只要加入我们要用的新模块.
最后我们要做的事情就是写一个main函数来启动我们的程序.
//如果编译中有关于main的错,就把加红色给去掉
#if YAKE_PLATFORM == PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char *argv[])
#endif
{
// Use default executor for convenience.
// It's always possible to manually execute TheApp::initialise() etc.
return (raf::runApplication( TheApp() )) ? 0 : 1;
}
这就是我们程序所需要的全部东西.
3.主要状态(Main state)
在这一课中我们只要使用一种状态,所有的事情都可以在里面完成.我们的main state将基于raf框架的RTMainState.像worlds creation,camera 和view port等很多事情,已经在基类中处理了,所以我们不用太担心它们.如果你好奇的话,可以去基类看看它们是怎么被处理的.剩下所有有趣的东西都将在这处理.
这个类如下所示:
//Main (and only for the moment) state of our application.
class YapMainState:public raf::RtMainState
{
public:
// YapMainState(raf::Application& owner); <-- this constructor has linking problems
YapMainState(raf::Application& owner) : raf::RtMainState(owner) {} // <- use this instead
protected:
virtual void onCreateScene(); //-> called at startup to build the scene.
virtual void onFrame(const real timeElapsed); //->called every time a new frame is drawn.
private:
//graphical world
yake::graphics::IWorld* mGWorld;
//Well, a few lights might not be so bad...
graphics::ISceneNode* mLightOneNode;
graphics::ILight* mLightOne;
//Light, from yonder sky, tis the sun! And an associated SceneNode, apparently.
graphics::ILight* mSunLight;
graphics::ISceneNode* mSunLightNode;
//Shadows
graphics::StringVector mShadowTechniques;
size_t mCurrentShadowTechnique;
//utils
yake::math::RandomNumberGeneratorMT mRandgen;
yake::math::Math mMath;
struct Bounds {
real leftBound;
real rightBound;
real lowerBound;
real upperBound;
};
//Let's define a simple class for easy access to a SceneNode and the associated
//Entity.
class SimpleObj {
public:
graphics::ISceneNode* pSN;
graphics::IEntity* pE;
real x,y,mSize,mHeight;
real verticalDirection, horizontalDirection;
Bounds bounds;
void setPosition (real _x, real _y)
{
x = _x;
y = _y;
if (x > bounds.rightBound) x = bounds.rightBound;
else if (x < bounds.leftBound) x = bounds.leftBound;
if (y > bounds.upperBound) y = bounds.upperBound;
else if (y < bounds.lowerBound) y = bounds.lowerBound;
if (pSN)
pSN->setPosition( Point3(x,mHeight,y));
}
void translate (real tx, real ty)
{
x += tx;
y += ty;
if (x > bounds.rightBound) x = bounds.rightBound;
else if (x < bounds.leftBound) x = bounds.leftBound;
if (y > bounds.upperBound) y = bounds.upperBound;
else if (y < bounds.lowerBound) y = bounds.lowerBound;
if (pSN)
pSN->setPosition( Point3(x,mHeight,y));
}
};
SimpleObj mPaddle1;
SimpleObj mPaddle2;
SimpleObj mBall;
SimpleObj mGroundPlane;
Bounds mArena;
real mBallSpeed;
real mPaddleSpeed;
private:
//Setup function used to create the scene.
void SetupGround(void);
void SetupVariables(void);
void SetupBall(void);
void SetupPaddle1(void);
void SetupPaddle2(void);
void SetupLights(void);
void SetupInput(void);
void SetupShadowTechnique(void);
//Input listeners
void onKey(const yake::input::KeyboardEvent & e);
void onMB(uint8 btn);
void onMMove(Vector3 mVector);
};
它有两个重要的函数:onCreateScene和onFrame.第一个函数在启动时被调用,所以我们可以用它来创建我们的场景,第二个函数在每一帧被渲染前调用.我们也定义了一个SimpleObj类,我们可以用它来处里我们的游戏中的物体的属性和完成一些基本操作(移动或平移).
4.创建场景
让我们看一下场景的创建:
void YapMainState::onCreateScene(void)
{
SetupVariables();
SetupInput();
mGWorld = getGraphicalWorld();
YAKE_ASSERT( mGWorld );
//Camera and viewport creation is done by base class.
// position camera
getDefaultCamera()->setFixedYawAxis(Vector3::kUnitY);
getDefaultCamera()->setPosition(Point3( 0, 1500, 10));
getDefaultCamera()->lookAt(Point3(0,0,0));
SetupBall();
SetupGround();
SetupPaddle1();
SetupPaddle2();
SetupLights();
mShadowTechniques = mGWorld->getShadowTechniques ();
SetupShadowTechnique();
mGWorld->setShadowsEnabled(true);
}
我们创建图形世界,一个视口,一个摄像机,一些光和阴影,最后还有我们的物体.
对于光,我们将使用一个静态的光源来照亮所有的物体和一个附在paddle1上的动态光源来创建一些在地面上的好看的阴影.
void YapMainState::SetupLights(void)
{
// fixed light (sun)
mSunLight = mGWorld->createLight();
mSunLightNode = mGWorld->createSceneNode();
mSunLightNode->attachLight(mSunLight);
mSunLight->setType(yake::graphics::ILight::LT_SPOT);
mSunLightNode->setPosition(Point3(1000,1250,500));
mSunLight->setSpotlightRange(30,50,1);
Point3 dir = -mSunLightNode->getPosition();
Vector3 vdir(dir.x, dir.y,dir.z);
mSunLight->setDirection(vdir.normalisedCopy());
mSunLight->setDiffuseColour(Color(0.35, 0.35, 0.38));
mSunLight->setSpecularColour(Color(0.9, 0.9, 1));
// movable light 1.
// This light will be attached to the paddle 1 node to create some shadows.
mLightOneNode = mPaddle1.pSN->createChildNode();
YAKE_ASSERT( mLightOneNode );
mLightOne = mGWorld->createLight();
YAKE_ASSERT( mLightOne );
mLightOneNode->attachLight( mLightOne );
mLightOne->setType( graphics::ILight::LT_POINT );
mLightOne->setDiffuseColour( Color(0.6,0.7,0.8) );
mLightOne->setSpecularColour( Color(1,1,1) );
mLightOne->setAttenuation( 8000, 1, 0.0005, 0 );
mLightOneNode->translate(Vector3( 0, 20, 0 ));
}
As a demonstration, we will let the user swap between shadows technique so we retrieve from the graphical world all the shadow technique possible and the scroll through them in the SetupShadowTechnique method:(不太懂,不好翻...)
void YapMainState::SetupShadowTechnique(void)
{
if (mShadowTechniques.empty())
return;
mCurrentShadowTechnique = ++mCurrentShadowTechnique % mShadowTechniques.size();
const String& name = mShadowTechniques[mCurrentShadowTechnique];
graphics::StringMap params;
params["tex_size"] = "1024";
params["tex_count"] = "3";
params["far_distance"] = "3000";
params["directional_light_extrusion_distance"] = "3000";
mGWorld->selectShadowTechnique( mShadowTechniques[mCurrentShadowTechnique], params );
if (name == "stencil_additive")
{
mSunLight->setCastsShadows( true );
mLightOne->setType(yake::graphics::ILight::LT_POINT);
mLightOne->setCastsShadows(true);
mLightOne->setDiffuseColour( Color(0.9,0.7,0.7) );
mLightOne->setSpecularColour( Color(1,1,1) );
mLightOne->setAttenuation(8000,1,0.0005,0);
}
else if (name == "stencil_modulative")
{
mSunLight->setCastsShadows( false );
mLightOne->setType(yake::graphics::ILight::LT_POINT);
mLightOne->setCastsShadows( true );
mLightOne->setDiffuseColour( Color(0.9,0.7,0.7) );
mLightOne->setSpecularColour( Color(1,1,1) );
mLightOne->setAttenuation(8000,1,0.0005,0);
}
else if (name == "texture_modulative")
{
mSunLight->setCastsShadows( true );
// Change fixed point light to spotlight
mLightOne->setType(yake::graphics::ILight::LT_SPOT);
mLightOne->setDirection(-Vector3::kUnitZ);
mLightOne->setCastsShadows(true);
mLightOne->setDiffuseColour( Color(0.9,0.7,0.7) );
mLightOne->setSpecularColour( Color(1,1,1) );
mLightOne->setAttenuation(8000,1,0.0005,0);
mLightOne->setSpotlightRange(80,90,1);
}
std::cout << "SHADOW TECHNIQUE: " << name.c_str() << "\n";
}
现在,让我们创建地板,scale it and position it :
void YapMainState::SetupGround(void)
{
// create entity
mGroundPlane.pE = mGWorld->createEntity("plane_1x1.mesh");
YAKE_ASSERT( mGroundPlane.pE );
mGroundPlane.pE->setCastsShadow( false );
// create scene node and attach entity to node
mGroundPlane.pSN = mGWorld->createSceneNode("root");
YAKE_ASSERT( mGroundPlane.pSN );
mGroundPlane.pSN->attachEntity( mGroundPlane.pE );
//Set size and position.
mGroundPlane.pSN->setScale( Vector3(1200,1,1200) );
mGroundPlane.pSN->setPosition (Point3(0,0,0));
//Set material.
mGroundPlane.pE->setMaterial("Examples/GrassFloor");
}
最后我们可以基于同样的步骤创建其它的物体.球在程序启动时将会有一个随机的方向.
void YapMainState::SetupBall(void)
{
mBall.pSN = mGWorld->createSceneNode();
YAKE_ASSERT( mBall.pSN );
mBall.pE = mGWorld->createEntity("Sphere_d1.mesh");
YAKE_ASSERT( mBall.pE );
mBall.pSN->setScale(Vector3(mBall.mSize,mBall.mSize,mBall.mSize));
mBall.pE->setCastsShadow( true );
mBall.pE->setMaterial("Examples/BumpMapping/MultiLightSpecular");
mBall.pSN->attachEntity( mBall.pE );
mBall.setPosition(mBall.x,mBall.y);
//Apply a random direction to the ball.
real dir = mRandgen () * 2 * mMath.PI;
mBall.horizontalDirection = mMath.Cos (dir);
mBall.verticalDirection = mMath.Sin (dir);
}
void YapMainState::SetupPaddle1(void)
{
// setup Paddle1
mPaddle1.pSN = mGWorld->createSceneNode();
YAKE_ASSERT( mPaddle1.pSN );
mPaddle1.pE = mGWorld->createEntity("cube.mesh");
YAKE_ASSERT( mPaddle1.pE );
mPaddle1.pE->setMaterial("2 - Default");
mPaddle1.pE->setCastsShadow( true );
mPaddle1.pSN->attachEntity( mPaddle1.pE );
mPaddle1.pSN->setScale( Vector3( 30./100., 100./100., mPaddle1.mSize/100.)); //original cube size is 100.
mPaddle1.setPosition( mPaddle1.x,mPaddle1.y);
}
void YapMainState::SetupPaddle2(void)
{
// setup Paddle2
mPaddle2.pSN = mGWorld->createSceneNode();
YAKE_ASSERT( mPaddle2.pSN );
mPaddle2.pE = mGWorld->createEntity("cube.mesh");
YAKE_ASSERT( mPaddle2.pE );
mPaddle2.pE->setMaterial("2 - Default");
mPaddle2.pE->setCastsShadow( true );
mPaddle2.pSN->attachEntity( mPaddle2.pE );
mPaddle2.pSN->setScale( Vector3( 30./100., 100./100., mPaddle1.mSize/10.)); //original cube size is 100.
mPaddle2.setPosition( mPaddle1.x,mPaddle1.y);
}
现在我们要初始化我们游戏的所有参数,为此我们只要给它加一个方法就好.
void YapMainState::SetupVariables(void)
{
//One function to cleanly define all of the variables
//CHALLENGE: Load all of the variables out of an XML config file.
mBallSpeed = 600;
mPaddleSpeed = 500.0;
mPaddle1.x = -500;
mPaddle1.y = 0;
mPaddle1.mSize = 200;
mPaddle1.mHeight = 50;
mPaddle1.bounds.lowerBound = -600+(mPaddle1.mSize/2);
mPaddle1.bounds.upperBound = 600-(mPaddle1.mSize/2);
mPaddle1.bounds.leftBound = mPaddle1.x;
mPaddle1.bounds.rightBound = mPaddle1.x;
mPaddle2.x = 500;
mPaddle2.y = 0;
mPaddle2.mSize = 200;
mPaddle2.mHeight = 50;
mPaddle2.bounds.lowerBound = -600+(mPaddle2.mSize/2);
mPaddle2.bounds.upperBound = 600-(mPaddle2.mSize/2);
mPaddle2.bounds.leftBound = mPaddle2.x;
mPaddle2.bounds.rightBound = mPaddle2.x;
mBall.x = 0;
mBall.y = 0;
mBall.mSize = 40;
mBall.mHeight = mBall.mSize / 2.;
mBall.bounds.lowerBound = -600+(mBall.mSize/2);
mBall.bounds.upperBound = 600-(mBall.mSize/2);
mBall.bounds.leftBound = -600+(mBall.mSize/2);
mBall.bounds.rightBound = 600-(mBall.mSize/2);
mArena.leftBound = -600;
mArena.rightBound = 600;
mArena.upperBound = 600;
mArena.lowerBound = -600;
}
5.添加键盘和鼠标监听
我们将允许用户改变摄像机的方向,按"S"键来改变阴景技术.按ESC键来退出应用程序.
第一,我们需要给键盘和鼠标的事件产生器注册我们的监听
void YapMainState::SetupInput(void)
{
// setup event input generators and bind them to the correct function.
getApp().getKeyboardEventGenerator()->subscribeToKeyDown( Bind1( &YapMainState::onKey, this ) );
getApp().getMouseEventGenerator()->subscribeToMouseButtonDown( Bind1( &YapMainState::onMB, this ) );
getApp().getMouseEventGenerator()->subscribeToMouseMoved( Bind1( &YapMainState::onMMove, this ) );
}
移动鼠标将会改变摄像机的方向,用下面的方法可以很容易的实现:
void YapMainState::onMMove(Vector3 mVector)
{
std::cout << "MouseMove.x: " << static_cast<int>( mVector.x ) << std::endl;
std::cout << "MouseMove.y: " << static_cast<int>( mVector.y ) << std::endl;
getDefaultCamera()->yaw(-( mVector.x ) * 0.13);
getDefaultCamera()->pitch(-( mVector.y ) * 0.13);
}
点击鼠标按键将会被记录在记录窗口:
void YapMainState::onMB(uint8 btn)
{
std::cout << "MB: " << static_cast<int>(btn) << std::endl;
}
最后是键盘监听:
void YapMainState::onKey(const yake::input::KeyboardEvent & e)
{
std::cout << "Key pressed: " << e.keyCode << "\n";
if (e.keyCode == input::KC_ESCAPE)
this->requestQuit();
else if (e.keyCode == input::KC_S)
this->SetupShadowTechnique();
}
paddles的移动和渲染的同步将会在onFrame方法中处理
6.让所有东西动起来
现在我们想要让的所有的东西动起来,因此我们将要进一步的观察onFrame方法:
void YapMainState::onFrame (const real timeElapsed)
{
real distance;
if ( getApp().getKeyboard() )
{
//Camera stuff.
distance = -200. * timeElapsed;
if ( getApp().getKeyboard()->isKeyDown(input::KC_LEFT))
getDefaultCamera()->moveRelative( distance*Vector3::kUnitX ); //Strafe Left (TS_LOCAL)
if ( getApp().getKeyboard()->isKeyDown(input::KC_RIGHT))
getDefaultCamera()->moveRelative( -distance*Vector3::kUnitX ); //Strafe Right (TS_LOCAL)
if ( getApp().getKeyboard()->isKeyDown(input::KC_UP))
getDefaultCamera()->moveRelative( distance*Vector3::kUnitZ );//Move Forward in TS_LOCAL
if ( getApp().getKeyboard()->isKeyDown(input::KC_DOWN))
getDefaultCamera()->moveRelative( -distance*Vector3::kUnitZ );//Move Backwards in TS_LOCAL
//Paddle movement.
distance = mPaddleSpeed * timeElapsed;
if ( getApp().getKeyboard()->isKeyDown(input::KC_A)) //paddle1 down
mPaddle1.translate (0,distance);
if ( getApp().getKeyboard()->isKeyDown(input::KC_Q)) //paddle 1up
mPaddle1.translate (0,-distance);
if ( getApp().getKeyboard()->isKeyDown(input::KC_L)) //paddle 2 down
mPaddle2.translate (0,distance);
if ( getApp().getKeyboard()->isKeyDown(input::KC_P)) //paddle 2 up
mPaddle2.translate (0,-distance);
}
//move ball !
distance = mBallSpeed * timeElapsed;
mBall.translate (mBall.horizontalDirection * distance,mBall.verticalDirection * distance);
//rotate the ball.
yake::math::Matrix3 RotMatrix;
yake::math::Quaternion RotQuat;
//Calculate rotation matrix depending of the ball speed and direction.
RotMatrix.FromEulerAnglesXYZ (mBall.verticalDirection * distance * 2 / mBall.mSize,0,-mBall.horizontalDirection * distance * 2 / mBall.mSize);
//Calculate quaternion from this matrix.
RotQuat.FromRotationMatrix (RotMatrix);
//apply rotation.
mBall.pSN->rotate (RotQuat);
//Check ball collision with arena.
if (((mBall.x + mBall.mSize / 2) >= mArena.rightBound) || ((mBall.x - mBall.mSize / 2) <= mArena.leftBound))
mBall.horizontalDirection = -mBall.horizontalDirection + mRandgen()*0.01; //invert horizontal direction and add some random stuff for fun.
if (((mBall.y + mBall.mSize / 2) >= mArena.upperBound) || ((mBall.y - mBall.mSize / 2) <= mArena.lowerBound))
mBall.verticalDirection = -mBall.verticalDirection + mRandgen()*0.01; //invert vertical direction and add some random stuff for fun.
//Check ball collision with paddle1. (works ok but not so realistic.)
real dx, dy;
dx = mMath.Abs (mBall.x-mPaddle1.x);
dy = mMath.Abs (mBall.y-mPaddle1.y);
//First, check if there is a collision
if ((dx <= (mBall.mSize / 2 +15)) && (dy <= (mBall.mSize / 2 + mPaddle1.mSize / 2)))
{
//calculate collision angle and compare with paddle diagonal.
real angle = mMath.ATan2 ((dy),(dx));
real diag = mMath.ATan2 (mPaddle1.mSize/2,15.);
std::cout << "ball collision with paddle 1, angle = " << angle << std::endl;
if ( (angle >= -diag) && (angle <= diag))
{
mBall.horizontalDirection = -mBall.horizontalDirection + (mRandgen()-0.5)*0.05; //invert horizontal direction and add some random stuff for fun.
//Move the ball outside the paddle.
if ((mBall.x-mPaddle1.x) > 0)
mBall.setPosition (mPaddle1.x + mBall.mSize / 2. + 15.,mBall.y);
else
mBall.setPosition (mPaddle1.x - mBall.mSize / 2. - 15.,mBall.y);
}
else
{
mBall.verticalDirection = -mBall.verticalDirection + (mRandgen()-0.5)*0.05; //invert vertical direction and add some random stuff for fun.
//Move the ball outside the paddle.
if ((mBall.y-mPaddle1.y) > 0)
mBall.setPosition (mBall.x,mPaddle1.y + mBall.mSize /2. + mPaddle1.mSize/2.);
else
mBall.setPosition (mBall.x,mPaddle1.y - mBall.mSize /2. - mPaddle1.mSize/2.);
}
}
//Check ball collision with paddle2. (works ok but not so realistic.)
dx = mMath.Abs (mBall.x-mPaddle2.x);
dy = mMath.Abs (mBall.y-mPaddle2.y);
//First, check if there is a collision
if ((dx <= (mBall.mSize / 2 +15)) && (dy <= (mBall.mSize / 2 + mPaddle2.mSize / 2)))
{
//calculate collision angle and compare with paddle diagonal.
real angle = mMath.ATan2 ((dy),(dx));
real diag = mMath.ATan2 (mPaddle2.mSize/2,15.);
std::cout << "ball collision with paddle 2, angle = " << angle << std::endl;
if ( (angle >= -diag) && (angle <= diag))
{
mBall.horizontalDirection = -mBall.horizontalDirection + (mRandgen()-0.5)*0.05; //invert horizontal direction and add some random stuff for fun.
//Move the ball outside the paddle.
if ((mBall.x-mPaddle2.x) > 0)
mBall.setPosition (mPaddle2.x + mBall.mSize / 2. + 15.,mBall.y);
else
mBall.setPosition (mPaddle2.x - mBall.mSize / 2. - 15.,mBall.y);
}
else
{
mBall.verticalDirection = -mBall.verticalDirection + (mRandgen()-0.5)*0.05; //invert vertical direction and add some random stuff for fun.
//Move the ball outside the paddle.
if ((mBall.y-mPaddle1.y) > 0)
mBall.setPosition (mBall.x,mPaddle2.y + mBall.mSize /2. + mPaddle2.mSize/2.);
else
mBall.setPosition (mBall.x,mPaddle2.y - mBall.mSize /2. - mPaddle2.mSize/2.);
}
}
}
第一件要做的事是得到用户的输入来移动摄像机或paddles.time elapsed的一个功能是被按比例放大或缩小移动,因此移动的速度并不依赖于帧率.
然后是更新球的转角和位置
最后是和墙和板的碰撞检测以及相应的更新球的位置和方向.
7.总结
这个例子十分简单,它无法展示出yake真正的有趣之处.实际上它可以单独用Ogre毫不费力的做出来.但是它的目的是用来为下一课用支持的,那时yake的有趣之处才会变得清晰.
Lythaniel
按顺序把代码粘进去编译会出错,因为有些东西在后面声明,而在前面就使用了,请读者自己调整位置.还有就是如果在别的文件夹创建的工程,记得要写配置文件(后缀为.cfg),还要注意common这个资源文件夹的路径,然后程序应该就能成功跑起来了.