From: Charles McGarvey Date: Wed, 13 Jan 2010 23:36:57 +0000 (-0700) Subject: finally fixed broken main loop X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=4f62ce947db282f0bbf4d49b3aafb83d7cf51adc;p=chaz%2Fyoink finally fixed broken main loop --- diff --git a/data/scenes/Classic.lua b/data/scenes/Classic.lua index 5709632..2ec9e02 100644 --- a/data/scenes/Classic.lua +++ b/data/scenes/Classic.lua @@ -757,7 +757,7 @@ DrawTilemap({ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ResetTransform() -Translate(-0.32, -0.28, -27) +Translate(-0.3, -0.3, -35) Scale(105, 52, 1) SetTexture("BackgroundFar") DrawTile() diff --git a/data/yoinkrc b/data/yoinkrc index 79fa707..d7f97cc 100644 --- a/data/yoinkrc +++ b/data/yoinkrc @@ -7,63 +7,67 @@ print "loading default settings..." -- 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 diff --git a/doc/yoink.6.in b/doc/yoink.6.in index fd00dad..ce90f1d 100644 --- a/doc/yoink.6.in +++ b/doc/yoink.6.in @@ -104,12 +104,7 @@ If true, double-buffering will be used to help minimize distortion and 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 @@ -121,9 +116,14 @@ the game may not be able to update the physics on schedule which could 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 @@ -150,7 +150,7 @@ need to. .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 @@ -201,7 +201,7 @@ Use the \fBdetail\fP option. The game world may look sparse or incomplete, 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 diff --git a/src/Character.cc b/src/Character.cc index 52a13e6..9da4344 100644 --- a/src/Character.cc +++ b/src/Character.cc @@ -43,7 +43,7 @@ public: { 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; @@ -79,7 +79,7 @@ private: Character::Character(const std::string& name) : - tilemap("Particles"), + tilemap(name), animation(name) { mState.init(); @@ -89,9 +89,9 @@ Character::Character(const std::string& name) : // 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); diff --git a/src/ErrorHandler.cc b/src/ErrorHandler.cc index 07bd6d7..8d0a8d9 100644 --- a/src/ErrorHandler.cc +++ b/src/ErrorHandler.cc @@ -54,6 +54,11 @@ std::string getErrorString(const Mf::Error& error) 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(); diff --git a/src/GameLayer.cc b/src/GameLayer.cc index 7a8e98c..46c3263 100644 --- a/src/GameLayer.cc +++ b/src/GameLayer.cc @@ -122,7 +122,7 @@ GameLayer::GameLayer() : 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); @@ -155,7 +155,7 @@ void GameLayer::update(Mf::Scalar t, Mf::Scalar dt) 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; @@ -193,9 +193,9 @@ void GameLayer::rayTimer() { 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; } } @@ -291,7 +291,7 @@ void GameLayer::setProjection() 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); } diff --git a/src/Heroine.cc b/src/Heroine.cc index 5df1031..a70b798 100644 --- a/src/Heroine.cc +++ b/src/Heroine.cc @@ -33,7 +33,7 @@ Heroine::Heroine() : - Character("Effects") {} + Character("Heroine") {} void Heroine::update(Mf::Scalar t, Mf::Scalar dt) diff --git a/src/MainLayer.cc b/src/MainLayer.cc index 568f0cd..5f097ea 100644 --- a/src/MainLayer.cc +++ b/src/MainLayer.cc @@ -142,11 +142,13 @@ void MainLayer::setupGL() 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() @@ -272,16 +274,18 @@ int main(int argc, char* argv[]) // 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; } @@ -333,6 +337,7 @@ int main(int argc, char* argv[]) 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"); diff --git a/src/Moof/Engine.cc b/src/Moof/Engine.cc index 50711c7..5d703da 100644 --- a/src/Moof/Engine.cc +++ b/src/Moof/Engine.cc @@ -55,10 +55,9 @@ public: 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 @@ -67,21 +66,21 @@ public: { 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); @@ -89,8 +88,7 @@ public: 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 { @@ -99,11 +97,10 @@ public: << 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)); @@ -112,12 +109,15 @@ public: 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() @@ -143,81 +143,55 @@ public: 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()); @@ -348,12 +322,12 @@ public: 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; + //} } @@ -369,10 +343,10 @@ public: std::list::iterator mStackIt; Scalar mTimestep; - Scalar mMaxFps; + Scalar mFramerate; int mFps; - bool mPrintFps; + bool mShowFps; }; @@ -381,46 +355,34 @@ Engine::Engine() : 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; } diff --git a/src/Moof/Engine.hh b/src/Moof/Engine.hh index 07e982a..28d7fa0 100644 --- a/src/Moof/Engine.hh +++ b/src/Moof/Engine.hh @@ -40,6 +40,9 @@ 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 @@ -55,20 +58,18 @@ public: 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 diff --git a/src/Moof/Error.hh b/src/Moof/Error.hh index 6dab32f..86b8a12 100644 --- a/src/Moof/Error.hh +++ b/src/Moof/Error.hh @@ -43,9 +43,11 @@ public: 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 diff --git a/src/Moof/Log.cc b/src/Moof/Log.cc index 9809771..3094642 100644 --- a/src/Moof/Log.cc +++ b/src/Moof/Log.cc @@ -35,7 +35,7 @@ namespace Mf { -Log::Level Log::gLevel = Log::WARNING; +Log::Level Log::gLevel = Log::INFO; void Log::setLevel(Level level) diff --git a/src/Moof/Settings.hh b/src/Moof/Settings.hh index d31b57d..6cd8629 100644 --- a/src/Moof/Settings.hh +++ b/src/Moof/Settings.hh @@ -72,18 +72,18 @@ public: void save() const; template - 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 -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(); diff --git a/src/Moof/Timer.cc b/src/Moof/Timer.cc index b428f0a..69d2405 100644 --- a/src/Moof/Timer.cc +++ b/src/Moof/Timer.cc @@ -152,6 +152,11 @@ bool Timer::isRepeating() const } +void Timer::fireIfExpired() +{ + fireIfExpired(getTicks()); +} + void Timer::fireIfExpired(Scalar t) { std::map::iterator it; diff --git a/src/Moof/Timer.hh b/src/Moof/Timer.hh index b657a5f..246b4b1 100644 --- a/src/Moof/Timer.hh +++ b/src/Moof/Timer.hh @@ -110,6 +110,7 @@ public: return gNextFire; } + static void fireIfExpired(); static void fireIfExpired(Scalar t); private: diff --git a/src/Moof/Video.cc b/src/Moof/Video.cc index 8dab056..ba375e1 100644 --- a/src/Moof/Video.cc +++ b/src/Moof/Video.cc @@ -355,6 +355,11 @@ Video::Attributes::Attributes() } settings.get("icon", icon); + settings.get("fullscreen", fullscreen); + settings.get("resizable", resizable); + settings.get("showcursor", cursorVisible); + settings.get("grab", cursorGrab); + std::vector dimensions; settings.get("videomode", dimensions); if (dimensions.size() > 1) @@ -362,12 +367,31 @@ Video::Attributes::Attributes() 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]; }