-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ResetTransform()
-Translate(-0.32, -0.28, -27)
+Translate(-0.3, -0.3, -35)
Scale(105, 52, 1)
SetTexture("BackgroundFar")
DrawTile()
-- Set the level of detail to use when drawing scenes. This can be 1, 2 or
-- 3, where 1 shows the least amount of detail and 3 draws the scene with
--- the most detail.
+-- the most detail. Lower levels of detail may improve performance on
+-- slower machines.
detail = 3
--- Set the number of times each second the physics state will be updated. A
--- value of 100 or higher is ideal for accurate physics approximations. Values
--- that are much lower cause the CPU to do less work, but accuracy will suffer.
--- Errors could be introduced in the game with extremely low values.
+-- Set the number of times each second the physics state will be updated.
+-- The quality of the physics simulation will increase as you increase this
+-- value, but the processor will be taxed more. Errors could be introduced
+-- in the game with extremely low values.
timestep = 80
--- Set the maximum number of frames that can be drawn per second. A value
--- of 50 is pretty good. If your computer is really old, you can get away
--- with decreasing this value and still have reasonably smooth animation.
--- You can set this to a very high number to effectively render as many
--- frames as is possible, but the actual rate could be limited by vertical
--- display synchronization, depending on the X11 driver and settings used.
--- You should not set this option higher than the point where the vertical
--- synchronization effectively limits the draw rate or else the game may
--- not be able to update the physics on schedule which could actually
--- significantly lower the quality of the animation.
+-- Set the target number of frames that should be drawn per second. The
+-- smoothness of the animation increases as you increase this value. You
+-- probably want to set this somewhere in the 25-85 range, depending on how
+-- much work you want your computer to do. For example, if you're on
+-- battery power, you might prefer 25 which is still reasonably smooth and
+-- will decrease battery drain significantly. You can also set this to a
+-- very high number to effectively draw as many frames as possible, but
+-- your actual framerate might be limited by the refresh rate of your
+-- display--use the swapcontrol setting to enable or disable this behavior.
+-- You can determine your actual framerate with the showfps option.
-maxfps = timestep / 2
+framerate = 50
--- Set whether or not to print the current actual framerate to the console.
+-- Set this to print the current actual framerate to the console each
+-- second.
-printfps = false
+showfps = true
--- Set whether or not the game will run in full-screen mode. If false, the
--- game will run in a window.
+-- Set this to run the game in full-screen mode. The default behavior is
+-- to run the game in a window.
-fullscreen = false
+fullscreen = true
--- If the game is running in a window, set whether or not the window will
--- be resizable.
+-- If the game is running in a window, set this to also make the window
+-- resizable. This has no effective if the fullscreen option is true.
resizable = true
--- Set the resolution or size of the window. The value is an array with
--- three number elements representing the width, height, and bits per pixel
--- that make up the video mode. A typical value is 800,600 for a size of
--- 800x600 pixels with millions of colors (the third number is optional).
+-- Set the screen resolution or size of the window. The value is an array
+-- with three number elements representing the width, height, and bits per
+-- pixel that make up the video mode. If the fullscreen option is set, the
+-- default behavior is to pick a native resolution. Otherwise, the game
+-- window will default to 800x600.
-videomode = {800, 600}
+--videomode = {800, 600}
--- Set whether or not the cursor will be visible when you mouse over the
--- display of the game.
+-- Set this to make the cursor remain visible as you mouse over the view of
+-- the game.
showcursor = true
--- Set whether or not the drawing should use two buffers. This results in
--- a higher quality animation. You should usually leave this as true.
+-- Set this to use double-buffering to improve animation quality. You
+-- should usually leave this as true.
doublebuffer = true
--- Set whether or not to sync with the display in order to reduce
--- distortion.
+-- Set this to sync with the refresh rate of your display. Your framerate
+-- will be limited to the refresh rate, but you may experience fewer ugly
+-- "artifacts" caused by the game animation.
swapcontrol = true
artifacts caused by the animation of the game. Otherwise, a single buffer
will be used. The default value is true.
.TP
-.B fullscreen
-If true, the window will capture the display and render the game in full
-screen splendor. A value of false means the game will run in a window.
-The default value is false.
-.TP
-.B maxfps
+.B framerate
The maximum number of frames to be drawn per second. If your computer is
really old, you can get away with decreasing this value and still have
reasonably smooth animation. You can set this to a very high number to
actually significantly lower the quality of the animation. The default
value is 40.
.TP
-.B printfps
+.B fullscreen
+If true, the window will capture the display and render the game in full
+screen splendor. A value of false means the game will run in a window.
+The default value is false.
+.TP
+.B showfps
If true, the current number of frames being drawn per second will be
-printed to the console. The default value is false.
+printed to the console every second. The default value is false.
.TP
.B resizable
If true, the window will be resizable by the window manager. This option
.SH EXAMPLES
Here are some examples of typical usage:
.TP
-$ yoink maxfps=60
+$ yoink framerate=60
Cap the allowable frame-rate to 60Hz.
.TP
$ yoink fullscreen=true
but that's probably better than choppy animation.
.TP
3. Decrease the timestep.
-You can set the \fBtimestep\fP to be as low as the your \fBmaxfps\fP
+You can set the \fBtimestep\fP to be as low as the your \fBframerate\fP
option. Remember the trade-off here is decreased simulation accuracy.
.PP
If you are having audio problems, you may need to upgrade OpenAL. Some
{
Mf::Vector2 x = state.position - location;
Mf::Scalar mag = x.length();
- Mf::Scalar d = 50.0;
+ Mf::Scalar d = 0.0;
// spring:
//mState.force += -15.0 * x - 1.5 * mState.velocity;
Character::Character(const std::string& name) :
- tilemap("Particles"),
+ tilemap(name),
animation(name)
{
mState.init();
// forces
mState.force = Mf::Vector2(0.0, 0.0);
- //mState.forces.push_back(SpringForce(Mf::Vector2(500.0, 200.0)));
+ //mState.forces.push_back(SpringForce(Mf::Vector2(20.0, 4.0)));
mState.forces.push_back(ResistanceForce(2.0));
- //mState.forces.push_back(Mf::LinearState<2>::GravityForce(-400.0));
+ //mState.forces.push_back(Mf::LinearState<2>::GravityForce(-9.8));
// starting position
mState.position = Mf::Vector2(5.0, 5.0);
str += ") could not be found.";
return str;
+ case Mf::Error::OPENAL_INIT:
+ str += "The audio library returned an error: ";
+ str += error.what();
+ return str;
+
case Mf::Error::RESOURCE_NOT_FOUND:
str += "A required resource (";
str += error.what();
0.1, Mf::Timer::REPEAT);
mState.heroine = Heroine::alloc();
- mState.heroine->animation.startSequence("GreenDiamond");
+ mState.heroine->animation.startSequence("FlyDiagonallyUp");
Mf::Scalar a[6] = {0.0, 1.5, -0.5, 3.0, -2.0, 1.0};
mState.interp.init(a, 5.0, Mf::Interpolator::OSCILLATE);
mState.scene->checkForCollision(*mState.heroine);
mState.camera.setPosition(Mf::Vector3(-mState.heroine->getState().position[0],
- -mState.heroine->getState().position[1], -6));
+ -mState.heroine->getState().position[1], -8));
//mState.camera.lookAt(Mf::promote(mState.heroine->getState().position));
mRay.point = mState.heroine->getState().position;
{
hits.front().normal.normalize();
mRay.solve(point, hits.front().distance);
- Mf::logInfo << "scene: d = " << hits.front().distance << std::endl;
- Mf::logInfo << " P = " << point << std::endl;
- Mf::logInfo << " n = " << hits.front().normal << std::endl;
+ //Mf::logInfo << "scene: d = " << hits.front().distance << std::endl;
+ //Mf::logInfo << " P = " << point << std::endl;
+ //Mf::logInfo << " n = " << hits.front().normal << std::endl;
}
}
void GameLayer::setProjection(Mf::Scalar width, Mf::Scalar height)
{
- mState.camera.setProjection(cml::rad(60.0), width / height, 1.0, 200.0);
+ mState.camera.setProjection(cml::rad(45.0), width / height, 1.0, 200.0);
}
Heroine::Heroine() :
- Character("Effects") {}
+ Character("Heroine") {}
void Heroine::update(Mf::Scalar t, Mf::Scalar dt)
glClearColor(0.0, 0.0, 0.0, 1.0);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(60.0, 1.33333, 1.0, 2500.0);
+ //glMatrixMode(GL_PROJECTION);
+ //glLoadIdentity();
+ //Mf::Scalar ratio = Mf::engine.getVideo()->getWidth() /
+ //Mf::engine.getVideo()->getHeight();
+ //gluPerspective(60.0, ratio, 1.0, 250.0);
- glMatrixMode(GL_MODELVIEW);
+ //glMatrixMode(GL_MODELVIEW);
}
void MainLayer::contextRecreated()
// make sure the engine started up okay
- if (Mf::engine.getError().isError())
+ const Mf::Error& error = Mf::engine.getError();
+ if (error.isError())
{
Mf::ModalDialog dialog;
dialog.title = PACKAGE_STRING;
- dialog.text1 = "Fatal Error";
- dialog.text2 = getErrorString(Mf::engine.getError());
+ dialog.text1 = "Uh oh!";
+ dialog.text2 = getErrorString(error);
dialog.type = Mf::ModalDialog::CRITICAL;
dialog.run();
- return 1;
+ // openal errors are not fatal
+ if (error.code() != Mf::Error::OPENAL_INIT) return 1;
}
Mf::Settings& settings = Mf::Settings::getInstance();
settings.loadFromFile(configFiles);
settings.parseArgs(argc, argv);
+ Mf::engine.initWithSettings(settings);
std::string iconFile = Mf::Resource::getPath(PACKAGE".png");
Impl() :
mError(Error::NONE),
mTimestep(0.01),
- mPrintFps(false)
+ mFramerate(0.02),
+ mShowFps(false)
{
- // first, initialize the libraries
-
#if defined(_WIN32) || defined(__WIN32__)
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
#else
{
const char* error = SDL_GetError();
mError.init(Error::SDL_INIT, error);
- //throw Exception(Error::SDL_INIT, error);
+ return; // fatal
}
else
{
- char vdName[128];
- SDL_VideoDriverName(vdName, sizeof(vdName));
+ char name[128];
+ SDL_VideoDriverName(name, sizeof(name));
logInfo << "initialized SDL; using video driver `"
- << vdName << "'" << std::endl;
+ << name << "'" << std::endl;
}
if (FE_Init() != 0)
{
const char* error = FE_GetError();
mError.init(Error::FASTEVENTS_INIT, error);
- //throw Exception(Error::FASTEVENTS_INIT, error);
+ return; // fatal
}
mAlDevice = alcOpenDevice(0);
if (!mAlDevice || !mAlContext)
{
const char* error = alcGetString(mAlDevice,alcGetError(mAlDevice));
- logError << "error while creating audio context: "
- << error << std::endl;
+ mError.init(Error::OPENAL_INIT, error);
}
else
{
<< alcGetString(mAlDevice, ALC_DEFAULT_DEVICE_SPECIFIER)
<< "'" << std::endl;
}
+ }
- // now load the settings the engine needs
-
- Settings& settings = Settings::getInstance();
-
+ bool initWithSettings(const Settings& settings)
+ {
unsigned randomSeed;
if (settings.get("rngseed", randomSeed)) srand(randomSeed);
else srand(time(0));
settings.get("timestep", timestep);
mTimestep = 1.0 / timestep;
- Scalar maxFps = 40.0;
- settings.get("maxfps", maxFps);
- mMaxFps = 1.0 / maxFps;
+ Scalar framerate = 40.0;
+ settings.get("framerate", framerate);
+ mFramerate = 1.0 / framerate;
capFps();
- settings.get("printfps", mPrintFps);
+ mShowFps = false;
+ settings.get("showfps", mShowFps);
+
+ return true;
}
~Impl()
void run()
{
- Scalar ticksNow = Timer::getTicks();
-
- Scalar nextStep = ticksNow;
- Scalar nextDraw = ticksNow;
- Scalar nextFpsUpdate = ticksNow + 1.0;
-
Scalar totalTime = 0.0;
- Scalar deltaTime = 0.0;
- Scalar accumulator = mTimestep;
+ Scalar ticks = Timer::getTicks();
+
+ Scalar nextUpdate = ticks;
+ Scalar nextDraw = ticks;
+ Scalar nextSecond = ticks + SCALAR(1.0);
mFps = 0;
- int frameAccum = 0;
+ int frames = 0;
+
+ const int MAX_FRAMESKIP = 15;
+ const Scalar inverseTimestep = SCALAR(1.0) / mTimestep;
do
{
- Scalar newTicks = Timer::getTicks();
- deltaTime = newTicks - ticksNow;
- ticksNow = newTicks;
-
- // don't slow the animation until 4Hz, which is unplayable anyway
- if (deltaTime >= 0.25) deltaTime = 0.25;
- accumulator += deltaTime;
-
- Timer::fireIfExpired(ticksNow);
+ Timer::fireIfExpired();
dispatchEvents();
- while (accumulator >= mTimestep)
+ int i = 0;
+ while (nextUpdate < Timer::getTicks() && i < MAX_FRAMESKIP)
{
- update(totalTime, mTimestep);
-
totalTime += mTimestep;
- accumulator -= mTimestep;
+ update(totalTime, mTimestep);
- nextStep += mTimestep;
- }
- if (ticksNow >= nextStep)
- {
- nextStep = ticksNow + mTimestep;
+ nextUpdate += mTimestep;
+ ++i;
}
- if (ticksNow >= nextDraw)
+ if (nextDraw < (ticks = Timer::getTicks()))
{
- frameAccum++;
+ ++frames;
+ draw((ticks + mTimestep - nextUpdate) * inverseTimestep);
+ mVideo->swap();
- if (ticksNow >= nextFpsUpdate) // determine the actual fps
- {
- mFps = frameAccum;
- frameAccum = 0;
+ nextDraw += mFramerate;
- nextFpsUpdate += 1.0;
- if (ticksNow >= nextFpsUpdate)
- {
- nextFpsUpdate = ticksNow + 1.0;
- }
-
- if (mPrintFps)
- {
- logInfo << mFps << " fps" << std::endl;
- }
- }
+ if (mShowFps && nextSecond < ticks)
+ {
+ mFps = frames;
+ frames = 0;
- draw(accumulator / mTimestep);
- mVideo->swap();
+ logInfo << mFps << " fps" << std::endl;
- nextDraw += mMaxFps;
- if (ticksNow >= nextDraw)
- {
- // we missed some scheduled draws, so reset the schedule
- nextDraw = ticksNow + mMaxFps;
+ nextSecond += SCALAR(1.0);
}
}
// be a good citizen and give back what you don't need
- Timer::sleep(std::min(std::min(nextStep, nextDraw),
- Timer::getNextFire()), Timer::ACTUAL);
+ Timer::sleep(0.0);
}
while (!mStack.empty());
void capFps()
{
- if (mMaxFps < mTimestep)
- {
- logWarning << "capping maximum fps to timestep ("
- << mTimestep << ")" << std::endl;
- mMaxFps = mTimestep;
- }
+ //if (mFramerate < mTimestep)
+ //{
+ //logWarning << "capping maximum fps to timestep ("
+ //<< mTimestep << ")" << std::endl;
+ //mFramerate = mTimestep;
+ //}
}
std::list<LayerP>::iterator mStackIt;
Scalar mTimestep;
- Scalar mMaxFps;
+ Scalar mFramerate;
int mFps;
- bool mPrintFps;
+ bool mShowFps;
};
mImpl(new Engine::Impl) {}
-const Error& Engine::getError() const
+bool Engine::initWithSettings(const Settings& settings)
{
// pass through
- return mImpl->mError;
+ return mImpl->initWithSettings(settings);
}
-
-void Engine::setVideo(VideoP video)
+const Error& Engine::getError() const
{
// pass through
- mImpl->mVideo = video;
-}
-
-VideoP Engine::getVideo() const
-{
- return mImpl->mVideo;
-}
-
-
-void Engine::setTimestep(int ts)
-{
- mImpl->mTimestep = 1.0 / Scalar(ts);
- mImpl->capFps();
+ return mImpl->mError;
}
-int Engine::getTimestep() const
+void Engine::clearError()
{
- return int(1.0 / mImpl->mTimestep);
+ // pass through
+ mImpl->mError.init(Error::NONE);
}
-void Engine::setMaxFps(int maxFps)
+void Engine::setVideo(VideoP video)
{
- mImpl->mMaxFps = 1.0 / Scalar(maxFps);
- mImpl->capFps();
+ // pass through
+ mImpl->mVideo = video;
}
-int Engine::getMaxFps() const
+VideoP Engine::getVideo() const
{
- return int(1.0 / mImpl->mMaxFps);
+ return mImpl->mVideo;
}
namespace Mf {
+class Settings;
+
+
/**
* The engine is essentially a stack of layers. While running, it updates each
* layer from the bottom up every timestep. It also draws each layer from the
Engine();
~Engine() {}
+
+ // loads settings: rngseed, timestep, framerate, showfps
+ bool initWithSettings(const Settings& settings);
const Error& getError() const;
+ void clearError();
// setting the video is required before you can run the engine and should
// probably be done before adding any layers
void setVideo(VideoP video);
VideoP getVideo() const;
- void setTimestep(int ts);
- int getTimestep() const;
-
- void setMaxFps(int maxFps); // draw rate is always capped at the timestep
- int getMaxFps() const;
-
int getFps() const;
void push(LayerP layer); // push a layer onto the top
enum Code
{
- NONE = 0,
+ UNINITIALIZED = -1, // -
+ NONE = 0, // -
ALC_INIT, // description
FASTEVENTS_INIT, // description
+ OPENAL_INIT, // description
FILE_NOT_FOUND, // path of missing file
RESOURCE_NOT_FOUND, // name of missing resource
SCRIPT_ERROR, // description
namespace Mf {
-Log::Level Log::gLevel = Log::WARNING;
+Log::Level Log::gLevel = Log::INFO;
void Log::setLevel(Level level)
void save() const;
template <typename T>
- bool get(const std::string& key, T& value);
+ bool get(const std::string& key, T& value) const;
private:
- Script mScript;
+ mutable Script mScript;
std::string mUserFile;
};
template <typename T>
-bool Settings::get(const std::string& key, T& value)
+bool Settings::get(const std::string& key, T& value) const
{
Script::Slot top = mScript[-1];
Script::Slot globals = mScript.getGlobalTable();
}
+void Timer::fireIfExpired()
+{
+ fireIfExpired(getTicks());
+}
+
void Timer::fireIfExpired(Scalar t)
{
std::map<unsigned,Timer*>::iterator it;
return gNextFire;
}
+ static void fireIfExpired();
static void fireIfExpired(Scalar t);
private:
}
settings.get("icon", icon);
+ settings.get("fullscreen", fullscreen);
+ settings.get("resizable", resizable);
+ settings.get("showcursor", cursorVisible);
+ settings.get("grab", cursorGrab);
+
std::vector<long> dimensions;
settings.get("videomode", dimensions);
if (dimensions.size() > 1)
mode[0] = dimensions[0];
mode[1] = dimensions[1];
}
- if (dimensions.size() > 2) mode[2] = dimensions[2];
+ else if (fullscreen)
+ {
+ SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
- settings.get("fullscreen", fullscreen);
- settings.get("resizable", resizable);
- settings.get("showcursor", cursorVisible);
- settings.get("grab", cursorGrab);
+ if (modes == (SDL_Rect**)0)
+ {
+ Mf::logError("no native video mode");
+ }
+ else if (modes == (SDL_Rect**)-1)
+ {
+ Mf::logWarning("any resolution allowed; choosing default 800x600");
+ mode[0] = 800;
+ mode[1] = 600;
+ }
+ else
+ {
+ while (*(modes + 1)) ++modes; // skip to the last
+
+ mode[0] = (*modes)->w;
+ mode[1] = (*modes)->h;
+ Mf::logInfo << "choosing native resolution "
+ << mode[0] << "x" << mode[1] << std::endl;
+ }
+ }
+ if (dimensions.size() > 2) mode[2] = dimensions[2];
}