Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

891531 Posts in 33546 Topics- by 24781 Members - Latest Member: smilingrob

June 19, 2013, 10:42:35 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Post your game loop
Pages: [1] 2 3 4
Print
Author Topic: Post your game loop  (Read 2299 times)
RobJinman
Level 0
***



View Profile WWW Email
« on: June 12, 2012, 01:00:38 AM »

This thread is just for fun, but maybe we'll learn something  Well, hello there!

Post your game loop exactly as it appears in your code (I mean real code, not pseudo-code).

Okay, here's mine:

Code:
   while (1) {
      m_win.doEvents();
      renderer.clear();
      m_eventManager.doEvents();
      renderer.drawPlainAlphaPoly(Vec2f(0, 0), m_zeroGRegion, Colour(0.3, 0.3, 0.2, 1.0), 0);
      EntityPhysics::update();
      drawAndUpdateItems();
      keyboard();
      deletePending();
      computeFrameRate();
      m_win.swapBuffers();
   }

As you can see, it's nothing special at all.

It'll be interesting to see how much variation there is, and how much can be gleaned about the actual games, if anything.
Logged

Moczan
Level 5
*****



View Profile
« Reply #1 on: June 12, 2012, 01:50:27 AM »

I use too much high-level stuff, my main loops:
Code:
KeyboardState keyboard = Keyboard.GetState();
moczan.utils.WonderKey.state = keyboard;

if(keyboard.IsKeyDown(Keys.Escape)){
Exit();
}

fsm.update();

base.Update(gameTime);
the same game also runs
Code:
renderer.update();
on separate thread

for AS3 project it's usually the same:
Code:
currentState.update();
Logged
eigenbom
Level 10
*****



View Profile WWW
« Reply #2 on: June 12, 2012, 02:03:59 AM »

blergh.. Waaagh!

Code:
while (Root::Instance()->window->isOpen())
{
    //--- LOADING SCREEN LOOP ---//

    if (!G->hasStartedGame){
      sf::Event event;
  while (Root::Instance()->window->pollEvent(event)) {
        if (event.type == sf::Event::Closed){
  Root::Instance()->window->close();
        }
      }

      if (loadScreenUpdateClock.getElapsedTime().asMilliseconds() > 50){
        loadScreenUpdateClock.restart();

        // check if the loading thread has finished
        double progress = 0;
        if (!G->hasLoadedCorePackage){
          G->hasLoadedCorePackage = Root::Instance()->resourceLoader->hasLoaded();
          if (G->hasLoadedCorePackage){
            Root::Instance()->finaliseCorePackage();
            setupGameThread.launch();
            progress = .5;
          }
          else {
            progress = .5 * Root::Instance()->resourceLoader->progress();
          }
        }
        else if (G->hasSetupGame){
          // initialise renderer
          Root::Instance()->renderer->setRenderTargetSize(Root::Instance()->window->getSize().x,Root::Instance()->window->getSize().y);
          Root::Instance()->renderer->setCameraZoom(Root::Instance()->camera->getZoom());
          G->hasStartedGame = true;
          progress = 1;

          // Boot script manager and run all scripts
          Root::Instance()->scriptManager->registerCoreIDs();
          Root::Instance()->scriptManager->requireAllScripts();
          Root::Instance()->scriptManager->runScriptOnLoads();       

          // create world data
          ((LuaWorld*)(Root::Instance()->world))->initialiseWorld();
          Root::Instance()->world->createAllChunks();
          ((LuaWorld*)(Root::Instance()->world))->finaliseWorld();

          // give moonman copies of all the blocks...
          int index = 0;
          // uint16 dirt = Root::Instance()->blockManager->getBlockInfo("dirt")->type;
          int inventorySize = (ID) ATTRIB(Root::Instance()->entitySystem->lookup(G->moonmanId),inventory_size);
          ID testBlockId = Root::Instance()->blockManager->getBlockInfo("test")->type;
          foreach(BlockInfo* bs, G->allBlockTypes){
            // LOG_DEBUG(*mm::log) << "Adding block: " << bs->name;
            if (bs->type!=BLT_AIR  && bs->type!=testBlockId){
              Root::Instance()->inventorySystem->insertBlocksAtIndex(G->moonmanId,(index+MOONMAN_HOTBAR_INDEX_OFFSET+5)%inventorySize,bs->type,(int)random(20,100));
              index++;
              if (index>inventorySize) break;
            }
        }

          // give an axe         
          {
            LOG_INFO(*mm::log) << "Giving moonman a big shiny axe";
            Root::Instance()->inventorySystem->insertItemsAtIndex(G->moonmanId,MOONMAN_HOTBAR_INDEX_OFFSET,Root::Instance()->itemManager->getItemType("item/axe"),1);
            Message m = Message::kWieldItem;
            m.wield_item.entity = G->moonmanId;
            m.wield_item.itemIndex = MOONMAN_HOTBAR_INDEX_OFFSET;
            Root::Instance()->queueMessage(G->moonmanId,m);
          }
          // resize gui
          G->gui->resize(Root::Instance()->displaySettings());
        }
        else {
          progress = .5 + .5*G->setupProgress;
        }

        Root::Instance()->window->clear(sf::Color::Black);
       
        // loading image
        loadingImage.setPosition(Root::Instance()->window->getSize().x/2.,Root::Instance()->window->getSize().y/2.);
        Root::Instance()->window->draw(loadingImage);

        // loading bar
        sf::Color loadingPink(244,87,142);
        sf::RectangleShape rect(sf::Vector2f(loadingTex.getSize().x*progress,16));
        rect.setPosition(loadingImage.getPosition().x - loadingTex.getSize().x/2., loadingImage.getPosition().y + loadingTex.getSize().y/2. + 16);
        rect.setFillColor(loadingPink);
        Root::Instance()->window->draw(rect);

        sf::RectangleShape outerRect(sf::Vector2f(loadingTex.getSize().x,16));
        outerRect.setPosition(rect.getPosition());
        outerRect.setFillColor(sf::Color::Transparent);
        outerRect.setOutlineColor(loadingPink);
        outerRect.setOutlineThickness(2);
        Root::Instance()->window->draw(outerRect);

        // flush
        Root::Instance()->window->display();
      }
      continue;
    }
    else {
       // fall into game loop below
    }

    //--- GAME LOOP ---//

    frameTimer.restart();

// Process Input and Events
sf::Vector2i mousePos = sf::Mouse::getPosition(*Root::Instance()->window);
gui::Widget::setMousePos(mousePos);

    {
      PROFILE("Input");

  sf::Event event;
  while (Root::Instance()->window->pollEvent(event))
  {
  // Close window and exit
  if (event.type == sf::Event::Closed){
  Root::Instance()->window->close();
          continue;
        }
        else if (event.type == sf::Event::Resized){
          // we may get a stream of these, so need to wait until the stream is finished..
          requestResize = true;
          lastResizeRequest = event.size;
          lastResizeRequestTimer.restart();         
          continue;
        }

        // Track focus
        static bool hasFocus = true;
  if (event.type == sf::Event::LostFocus){
  hasFocus = false;
  }
  else if (event.type == sf::Event::GainedFocus){
  hasFocus = true;
  }

  // handle event
  if (hasFocus){
          // handle gui events
          if (G->gui->handleEvent(*Root::Instance()->window,event)) continue;

          // pass through ..
          switch(event.type){
            case sf::Event::MouseButtonPressed:
            case sf::Event::MouseButtonReleased: {
              handleMouseButton(event);
              break;
            }
            case sf::Event::KeyPressed: {
              handleKeyPressed(event);
              break;
            }
          }
  }
      }
    }

    // handle resize
    if (requestResize && lastResizeRequestTimer.getElapsedTime().asMilliseconds() > 500){
      requestResize = false;
      Root::Instance()->windowResized(lastResizeRequest.width,lastResizeRequest.height);
      // LOG_DEBUG(*mm::log) << "Root::Instance()->displaySettings() = " << Root::Instance()->displaySettings().windowWidth << "x" << Root::Instance()->displaySettings().windowHeight;

      G->gui->resize(Root::Instance()->displaySettings());
    }
   
/// UPDATE
/*
    /// TODO: do fs check
if (fileSystemClock.GetElapsedTime() > 500){
G->fileSystemWatcher->check();
fileSystemClock.Reset();
}
*/
double realDt = DT*G->timeMultiplier;

static mm::Pass passes[] = {kPassRead, kPassExecute, kPassWrite};
static ISystemBase* orderedSystems[] = {
Root::Instance()->physicsSystem,
        G->moonmanController.get(),
Root::Instance()->spriteSystem,
Root::Instance()->compositeSpriteSystem,
Root::Instance()->inventorySystem,
Root::Instance()->worldObjectSystem,
Root::Instance()->animalController
};
   
    // update systems
    foreach(mm::Pass p, passes){
foreach (ISystemBase* sys, orderedSystems){
  sys->update(p, realDt);
}
    }

    // update gui
G->gui->update(*Root::Instance()->window, realDt);   

    // process messages   
    {
      if (G->showSystemMessages){
        G->haveShownMessageSeparator = false;
      }

      PROFILE("Messages");
      foreach(ISystemBase* sys, orderedSystems){
        processMessages(sys);
      }
      processRootMessages();
    }
   
     // place the tile in the world
    if (G->editMode && G->isPlacingTile){
     placeTile();
    }
    else if (!G->editMode && G->isMining){
      mineTile();
    }
    else if (!G->editMode && G->isBlocking){
      useBlock();
    }

    if (G->editMode){
      int dx = 0, dy = 0;
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) dx = -1;
      else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) dx = 1;

      if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) dy = 1;
      else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) dy = -1;
     
      // Restrict edit focus center to world bounds
      if ((G->editFocus.x>(uint32)CHUNK_SIZE && dx<0) || (G->editFocus.x<(uint32)(CHUNK_SIZE*(Root::Instance()->world->chunksWide()-1)) && dx>0))
        G->editFocus.x += dx;

      if ((G->editFocus.y>(uint32)CHUNK_SIZE && dy<0) || (G->editFocus.y<(uint32)(CHUNK_SIZE*(Root::Instance()->world->chunksHigh()-1)) && dy>0))
       G->editFocus.y += dy;
    }

updateCamera(realDt);

// RENDER   
    if (!requestResize)
    {
      PROFILE("Rendering");

      Root::Instance()->renderer->update(Root::Instance()->window,Root::Instance()->world,Root::Instance()->camera,realDt);
      Root::Instance()->renderer->render(Root::Instance()->window,Root::Instance()->world,Root::Instance()->camera);
         
  // draw ui
      Root::Instance()->window->setView(Root::Instance()->window->getDefaultView());
      if (G->showUI){
        renderUI();
      }
    }
    else {
      Root::Instance()->window->clear();
    }

    if (G->enableProfiling){
      static bool texSetup = false;
      static sf::RenderTexture tex;
      static sf::Clock clock;
      if (!texSetup){
        tex.create(1024,1024);
        tex.clear(sf::Color::Transparent);
        texSetup = true;
      }

      {
        //PROFILE("Profiling");
        functionProfileVisualiser.update(realDt,frameTimer.getElapsedTime().asMicroseconds());     

        if (clock.getElapsedTime().asMilliseconds() > 400){
          tex.clear(sf::Color::Transparent);
          functionProfileVisualiser.render(tex);
          tex.display();
          clock.restart();
        }
     
        sf::Sprite profileOverlay(tex.getTexture());
        profileOverlay.setPosition(0,0);
        Root::Instance()->window->draw(profileOverlay);
      }
    }

// flush
Root::Instance()->window->display();
   
}
Logged

HernanZh
Level 2
**


hernizhou@hotmail.com
View Profile WWW Email
« Reply #3 on: June 12, 2012, 02:30:59 AM »

I don't remember where I stole the frame limiter code from
Code:
void zGame::execute()
{
    //framelimiter variables
    double t = 0.0;
    double dt = 1.0/60.0;
    double accumulator = 0.0;
    double deltaTime;
    uint64 startTime=timeGetTime();
    uint64 endTime=0;
    //fps counter
    double second=0;
    int refresh=0;

    //start main loop
    while (rungame)
    {
        //Frame limiter
        endTime=timeGetTime();
        deltaTime=(endTime-startTime)/1000.0; //convert to second
        startTime=endTime;
        accumulator+=deltaTime;

        //calculate screen refresh rate
        second+=deltaTime;
        if (second>1)
        {
            sprintf(refreshrate,"%2.1f fps",refresh/second);
            refresh=0;
            second=0;
        }

        //resuming from suspend -> pause game
        if(resumeFromSuspend())
        {
            //clear accumulator
            accumulator=0;
            //send pause call
            currentFrame->paused=true;
        }

        //Catch events
        events();

        //Main gameplay loop
        while (accumulator>=dt)
        {
            //Inner game loop
            controller->setState();
            currentFrame->update();
            currentFrame->play();
            //Update framelimiter variables
            t += dt;
            accumulator -= dt;

            //Render in the last loop
            if (accumulator<dt)
            {
                render();
                //screen refresh rate
                refresh++;
                //sleep the remaining time
                sleep((int)accumulator*1000);
            }
        }

        //check end of level
        if (currentFrame->run==false)
        {
            endFrame();
            //prevent frame skipping (initialization may cause short freeze)
            startTime=timeGetTime();
        }
    }
    //reached the end without releasing the level
    if (currentFrame!=NULL)
    {
        delete currentFrame;
        currentFrame=NULL;
    }
}

Logged

Klaim
Level 10
*****



View Profile WWW
« Reply #4 on: June 12, 2012, 02:56:04 AM »

From my old prototype RLC:

http://code.google.com/p/radiant-laser-cross/source/browse/RadiantLaserCross/RLC_Game.cpp
Code:
void Game::run()
        {
                RLC_LOG << "Starting " << GAME_TITLE ;

                initialize();

                m_running = true;

                while( is_running() && display().IsOpened() )
                {
                        update();
                        render();
                }

                terminate();
        }

Then
Code:
void Game::update()
        {
                GC_ASSERT_NOT_NULL( m_system );

                // process window events
                sf::Event Event;
                auto& window = display();
                while ( window.GetEvent(Event) )
                {
                        // Close window : exit
                        if (Event.Type == sf::Event::Closed)
                                window.Close();
                }

                // perform each tick that passed since last tick!
                const float time_since_start = m_system->clock().GetElapsedTime();
                float time_since_last_tick = time_since_start - m_last_tick_time;

                while( time_since_last_tick > config::TICK_TIME )
                {
                        m_last_tick_time = time_since_start;
                        time_since_last_tick -= config::TICK_TIME;

                        one_game_tick();
                }

        }

And then

Code:
void Game::render()
        {
                GC_ASSERT_NOT_NULL( m_system );

                // graphic rendering

                auto& window = display();
                window.Clear( sf::Color( 0, 0, 80, 0 ) );

                m_gamestate_manager.render();

                window.Display();

                // audio rendering

                // TODO : add audio update here
        }


Note: this is very very bad. I have better implementations at home.
Logged

http://www.klaimsden.net | Game : NetRush | Digital Story-Telling Technologies : Art Of Sequence
_Tommo_
Level 7
**


frn frn frn


View Profile WWW
« Reply #5 on: June 12, 2012, 04:42:45 AM »

This may be a little different from the others:

Engine start code, on Mac:
Code:
void OSXPlatform::loop( float frameTime )
{
// start animation timer
NSTimer* timer = [NSTimer timerWithTimeInterval:( frameTime )
target:view
selector:@selector(stepCallback:)
userInfo:nil
repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; // ensure timer fires during resize

running = true;

//start event dispatching loop and give control to Cocoa
[[NSApplication sharedApplication] run];
}

On iOS:
Code:
- (void) startAnimation
{
if (!animating)
{
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView:)];

[displayLink setFrameInterval: platform->getGame()->getNativeFrameLength() ];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

animating = TRUE;
}
//reset frametimer to avoid huge time differences after restoring!
frameTimer.reset();

[self becomeFirstResponder];
}

On Windows, I use Poco to emulate the Timer
Code:
void Win32Platform::loop( float frameTime )
{
frameInterval = frameTime;

frameTimer.reset();

//start timer thread
Poco::Timer frameCaller( 0, frameInterval * 1000.f );
    frameCaller.start( *this );

Timer timer;
running = true;
while( running )
{
if( frameInterval > 0 )
frameStart.wait();

while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
running = false;

TranslateMessage( &msg );
DispatchMessage( &msg );
}

//never send a dt lower than the minimum!
float dt = min( game->getMaximumFrameLength(), (float)timer.deltaTime() );

step( dt );
}
}


///this function is called by Poco::Timer on the timer thread
void invoke( void* platform )
{
    frameStart.set(); //enable the main loop to run once
}

And then on every platform, each frame interval step() is called, which computes one frame.

Code:
void ApplePlatform::step( float dt )
{
Timer frameTimer;

//clamp to max dt to avoid crazy behaviour
dt = Math::min( dt, game->getMaximumFrameLength() );

    game->loop(dt);
    
    render->render();
    sound->update(dt);

realFrameTime = frameTimer.getElapsedTime();
}

I do all the crazyness about the timers to have a solid 60-fps game, no more and no less. With any other method you can't just be sure (ok, except for vsync, that is).
Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW Email
« Reply #6 on: June 12, 2012, 04:47:23 AM »

Code:
function Main_Loop return Integer is
    -- Calculate the end of frame time.
    End_Of_Frame: Time := Clock + Common.Frame_Delay;
begin
    -- Calculate the new delta.
    Tickables.Calculate_Delta;

    declare
        Next_State: Logic_Access;
    begin
        -- Process the logic, obtaining the next logic.
        Current_State.Process(Next_State);

        -- If the logic has changed...
        if Next_State /= Current_State then
            -- Release the old logic.
            Free(Current_State);
            -- Store the new one.
            Current_State := Next_State;

            -- A state of null is a request to die.
            if Current_State = null then
                return 0;
            end if;
        end if;
    end;

    -- Draw the screen.
    Renderer.Draw;

    if Common.Frame_Delay > 0.0 then
        delay until End_Of_Frame;
    end if;

    return 1;

exception
    when Error: others =>
        Error_Out(Error);

        return 0;
end Main_Loop;

Not a lot to say really, it does what it does.
Logged

Franchise - The restaurant wars begin!

What would John Carmack do?
kamac
Level 10
*****


Notorious posts editor


View Profile WWW Email
« Reply #7 on: June 12, 2012, 04:51:40 AM »

eigenbom, that is alot of pointers  Waaagh!


But I have the suckiest, un-organized loop anyway.


Code:
void app::GameLoop()
{
mana_bar->Update();

//////////////////////////////////////////////////////////////////////////////
//                              CAMERA EVENTS                               //
//////////////////////////////////////////////////////////////////////////////
//Set the viewport to look at the player
if(float(agk::GetViewOffsetX() - (agk::GetSpriteX(player_sprite)-400)) > 0.5 ||
float(agk::GetViewOffsetX() - (agk::GetSpriteX(player_sprite)-400)) < -0.5)
agk::SetViewOffset(agk::GetSpriteX(player_sprite)-400,agk::GetViewOffsetY());
if(float(agk::GetViewOffsetY() - (agk::GetSpriteY(player_sprite)-240)) > 0.5 ||
float(agk::GetViewOffsetY() - (agk::GetSpriteY(player_sprite)-240)) < -0.5)
agk::SetViewOffset(agk::GetViewOffsetX(),agk::GetSpriteY(player_sprite)-240);
//agk::SetViewOffset(agk::GetSpriteX(player_sprite)-400,agk::GetSpriteY(player_sprite)-240);
//////////////////////////////////////////
// CHECK IF THE CAMERA IS OUT OF BOUNDS //
//////////////////////////////////////////
//Less than left?
if(agk::GetViewOffsetX() < min_boundx)
agk::SetViewOffset((float)min_boundx,agk::GetViewOffsetY());
//Less than top?
if(agk::GetViewOffsetY() < min_boundy)
agk::SetViewOffset(agk::GetViewOffsetX(),(float)min_boundy);
//More than right?
if((agk::GetViewOffsetX()+800-64) > max_boundx)
agk::SetViewOffset(float(max_boundx-800+64),agk::GetViewOffsetY());
//More than bottom?
if((agk::GetViewOffsetY()+480-64) > max_boundy)
agk::SetViewOffset(agk::GetViewOffsetX(),float(max_boundy-480+64));
//////////////////////////////////////////////////////////////////////////////
//                         END OF CAMERA EVENTS                             //
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//                         PLAYER MOVEMENT EVENT                            //
//////////////////////////////////////////////////////////////////////////////

if(agk::GetVirtualButtonState(2)) //Right
{
if(gamelevel != 30)
agk::SetSpritePhysicsVelocity(player_sprite,100,
agk::GetSpritePhysicsVelocityY(player_sprite));
else
agk::SetSpritePhysicsVelocity(player_sprite,100/cool_down,
agk::GetSpritePhysicsVelocityY(player_sprite));
//Animation.
if(!agk::GetSpritePlaying(player_sprite))
agk::PlaySprite(player_sprite,5.0f,0,0,2);
} else {
if(agk::GetVirtualButtonState(3)) //Left
{
if(gamelevel != 30)
agk::SetSpritePhysicsVelocity(player_sprite,-100,
agk::GetSpritePhysicsVelocityY(player_sprite));
else
agk::SetSpritePhysicsVelocity(player_sprite,-100/cool_down,
agk::GetSpritePhysicsVelocityY(player_sprite));
//Animation.
if(!agk::GetSpritePlaying(player_sprite))
agk::PlaySprite(player_sprite,5.0f,0,3,4);
} else {
//Else, stop our sprite from moving left/right.
agk::SetSpritePhysicsVelocity(player_sprite,0,
agk::GetSpritePhysicsVelocityY(player_sprite));
}
}
/////////////
// JUMPING //
/////////////
if(agk::GetVirtualButtonState(1)) //Jump
{
if(agk::PhysicsRayCast(
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2, /* X */
agk::GetSpriteY(player_sprite)+32,                                    /* Y */
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2, /* VX*/
agk::GetSpriteY(player_sprite)+65) > 0 ||
agk::PhysicsRayCast(
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2-20, /* X */
agk::GetSpriteY(player_sprite)+32,                                    /* Y */
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2-20, /* VX*/
agk::GetSpriteY(player_sprite)+65) > 0 ||
agk::PhysicsRayCast(
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2+20, /* X */
agk::GetSpriteY(player_sprite)+32,                                    /* Y */
agk::GetSpriteX(player_sprite)+agk::GetSpriteWidth(player_sprite)/2+20, /* VX*/
agk::GetSpriteY(player_sprite)+65) > 0)/*VY*/
{
if(agk::GetSpritePhysicsVelocityY(player_sprite) > -5)
agk::SetSpritePhysicsVelocity(player_sprite,agk::GetSpritePhysicsVelocityX(player_sprite),
-300);
}
}
//////////////////////////////////////////////////////////////////////////////
//                      END OF PLAYER MOVEMENT EVENT                        //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                         ROOM COLLISIONS & EVENTS                         //
//////////////////////////////////////////////////////////////////////////////

//Handle room special events.
//...                                                                                 - TODO

//Picking up the key.
if(agk::GetSpriteCollision(player_sprite,map_sprite[1][key_y][key_x]))
{
//Player has picked up the key.
agk::SetSpriteVisible(map_sprite[1][key_y][key_x],0);
picked_key = true;
}
//Exitting the level.
if(picked_key)
{
if(agk::GetSpriteCollision(player_sprite,map_sprite[0][exit_y][exit_x]) ||
agk::GetSpriteCollision(player_sprite,map_sprite[0][exit_y][exit_x+1]))
{
loading->fadein();
agk::SetVirtualButtonVisible(1,0);
agk::SetVirtualButtonVisible(2,0);
agk::SetVirtualButtonVisible(3,0);
}
}

/////////////////////
// CHECK FOR TRAPS //
/////////////////////
if(agk::PhysicsRayCast(agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)+32,
agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)+65))
{
//Down-raycast.
int raycast_spriteid = agk::GetRayCastSpriteID();
int rc_img = agk::GetSpriteImageID(raycast_spriteid);
if(rc_img == sheet_subimage[53] ||
rc_img == sheet_subimage[54] ||
rc_img == sheet_subimage[7])
{
//Lava || Spikes.
KillPlayer();
}
}
//Separate collisions for different spikes.
//Left-wall spikes.
if(agk::PhysicsRayCast(agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)+32,
agk::GetSpriteX(player_sprite)-1,agk::GetSpriteY(player_sprite)+32))
{
//Left-raycast
int raycast_spriteid = agk::GetRayCastSpriteID();
int rc_img = agk::GetSpriteImageID(raycast_spriteid);
if(rc_img == sheet_subimage[11])
{
//Lava || Spikes.
KillPlayer();
}
if(rc_img != magicblock_image && rc_img != sheet_subimage[9])
{
if(agk::GetSpritePhysicsVelocityX(player_sprite) < 0)
agk::SetSpritePhysicsVelocity(player_sprite,0,agk::GetSpritePhysicsVelocityY(player_sprite));
}

}
//Right-wall spikes.
if(agk::PhysicsRayCast(agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)+32,
agk::GetSpriteX(player_sprite)+65,agk::GetSpriteY(player_sprite)+32))
{
//Right-raycast
int raycast_spriteid = agk::GetRayCastSpriteID();
int rc_img = agk::GetSpriteImageID(raycast_spriteid);
if(rc_img == sheet_subimage[12])
{
//Lava || Spikes.
KillPlayer();
}
if(rc_img != magicblock_image && rc_img != sheet_subimage[9])
{
if(agk::GetSpritePhysicsVelocityX(player_sprite) > 0)
agk::SetSpritePhysicsVelocity(player_sprite,0,agk::GetSpritePhysicsVelocityY(player_sprite));
}
}
//Top-wall spikes.
if(agk::PhysicsRayCast(agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)+32,
agk::GetSpriteX(player_sprite)+32,agk::GetSpriteY(player_sprite)-1))
{
//Top-raycast
int raycast_spriteid = agk::GetRayCastSpriteID();
int rc_img = agk::GetSpriteImageID(raycast_spriteid);
if(rc_img == sheet_subimage[13])
{
//Lava || Spikes.
KillPlayer();
}
}
int colliding = 0;
for(int n=0; n<mana_dispenser_slot; n++)
{
if(agk::GetSpriteDistance(player_sprite,map_sprite[1][mana_dispenser_y[n]][mana_dispenser_x[n]]) < 25.0f)
{
mana_bar->SetValue(50);
agk::SetSpriteColor(player_sprite,180,180,255,255);
colliding = 1;
}
}
if(!colliding)
{
agk::SetSpriteColor(player_sprite,255,255,255,255);
}

//////////////////////////////
// CHECK IF IT'S LAST LEVEL //
//////////////////////////////
if(gamelevel == 30)
{
int truey = magicbook_y/64;
int truex = magicbook_x/64;
int player_x = agk::GetSpriteX(player_sprite);
int player_y = agk::GetSpriteY(player_sprite);
int xd = player_x - truex;
int yd = player_y - truey;
cool_down = sqrt((float)xd*(float)xd+(float)yd*(float)yd)/1000.0f;
if(cool_down <= 0.0f)
cool_down = 0.1f;
}

//////////////////////////////////////////////////////////////////////////////
//                        END OF ROOM COLLISIONS & EVENTS                   //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                         CREATING BLOCKS! MAGIC                           //
//////////////////////////////////////////////////////////////////////////////

//Check if the user has pressed on the screen. (Starting block creation)
if(agk::GetPointerPressed() && can_spawn_blocks)
{
drawing_block = true;
// Create drawing block's ghost. (transparent, gray)
drawing_block_ghost = agk::CreateSprite(0);
//Set position.
agk::SetSpritePosition(drawing_block_ghost,agk::GetPointerX()+agk::GetViewOffsetX(),
agk::GetPointerY()+agk::GetViewOffsetY());
//... Size, color & depth.
agk::SetSpriteSize(drawing_block_ghost,100.0f,100.0f);
agk::SetSpriteColor(drawing_block_ghost,200,200,200,100);
agk::SetSpriteDepth(drawing_block_ghost,2);
}
//Proceed while user is drawing the block.
if(drawing_block && can_spawn_blocks)
{
float size_x = (agk::GetPointerX()+agk::GetViewOffsetX())-agk::GetSpriteX(drawing_block_ghost);
float size_y = (agk::GetPointerY()+agk::GetViewOffsetY())-agk::GetSpriteY(drawing_block_ghost);
float sprite_x = agk::GetSpriteX(drawing_block_ghost);
float sprite_y = agk::GetSpriteY(drawing_block_ghost);
float sprite_width = agk::GetSpriteWidth(drawing_block_ghost);
float sprite_height = agk::GetSpriteHeight(drawing_block_ghost);
if(size_x < 0)
size_x *= -1;
if(size_y < 0)
size_y *= -1;
agk::SetSpriteSize(drawing_block_ghost,size_x,size_y);
//Determine if the block is colliding.


int mana_burn = int((size_x*size_y)/450);
mana_bar->SetValueTemp(mana_bar->GetValue()-mana_burn);
//Check if we have enough mana.
if(mana_bar->GetValue()-mana_burn <= 0)
{
agk::SetSpriteColor(drawing_block_ghost,200,50,50,100);
} else {
//Check for collision with our magic block. If there is any collision, turn it to red.
if(agk::PhysicsRayCast(sprite_x,sprite_y,sprite_x+sprite_width,sprite_y+sprite_height))
{
agk::SetSpriteColor(drawing_block_ghost,200,50,50,100);
} else {
//If the square is too small...
if(size_x < 10 || size_y < 10)
{
agk::SetSpriteColor(drawing_block_ghost,200,50,50,100);
} else {
agk::SetSpriteColor(drawing_block_ghost,200,200,200,100);
}
}
}
if(agk::GetPointerReleased() && can_spawn_blocks)
{
//The user wants to stop drawing the block.
drawing_block = false;
//At first, determine if we had enough mana to draw the block,
// by checking drawing_block_ghost color.
if(agk::GetSpriteColorGreen(drawing_block_ghost) != 50) //We had enough mana && our block won't
//collide with anything.
{
//Play sound
agk::PlaySound(sound_index[1],100,0,2);
//Drain mana.
mana_bar->SetValue(mana_bar->GetValue()-mana_burn);
mana_bar->Shake(87.0f);
//////////////////////////
/////// DRAW BLOCK ///////
//////////////////////////
int free_magicblock_slot = GetMagicBlockFreeSlot();
agk::SetSpriteAngle(magicblock[free_magicblock_slot],0.0f);
agk::SetSpriteColorAlpha(magicblock[free_magicblock_slot],255);
agk::SetSpritePosition(magicblock[free_magicblock_slot],sprite_x,sprite_y);
agk::SetSpriteVisible(magicblock[free_magicblock_slot],1);
agk::SetSpriteSize(magicblock[free_magicblock_slot],sprite_width,sprite_height);
agk::SetSpritePhysicsOn(magicblock[free_magicblock_slot],2);
magicblock_live[free_magicblock_slot] = MAGICBLOCK_TIME;
magicblock_slot[free_magicblock_slot] = 1;
//Check if it's last level.
if(gamelevel == 30)
agk::SetSpriteColor(magicblock[free_magicblock_slot],rand()%256,rand()%256,rand()%256,255);
else
agk::SetSpriteColor(magicblock[free_magicblock_slot],255,255,255,255);
} else {
mana_bar->SetValue(mana_bar->GetValue());
mana_bar->Shake(71.0f);
}

agk::DeleteSprite(drawing_block_ghost);
}
}
//Make sure every block will slowly dissapear.
ProcessMagicBlocks();
//////////////////////////////////////////////////////////////////////////////
//                         END OF CREATING BLOCKS! MAGIC                    //
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//                         MANA PROCESSING                                  //
//////////////////////////////////////////////////////////////////////////////

//Update our timer.
mana_processing_timer = agk::Timer();
//Check for delay between adding mana point.
if(mana_processing_timer - mana_timer > 1.0f)
{
//Store our timer value again.
mana_timer = agk::Timer();
//Make sure we haven't reached mana limit first.
if(mana_bar->GetValue() < 50)
{
//Add one mana point.
mana_bar->SetValue(mana_bar->GetValue()+1);
mana_bar->Shake(75.0f);
}
}
//Check if mana is below 0. If yes, get it to 0.
if(mana_bar->GetValue() < 0)
mana_bar->SetValue(0);

//////////////////////
// UPDATE MANA TEXT //
//////////////////////
agk::SetTextString(mana_level_text,agk::Str(mana_bar->GetValue()));
agk::SetTextPosition(mana_level_text,
mana_bar->GetPosX()+(128/2-agk::GetTextTotalWidth(mana_level_text)/2),
mana_bar->GetPosY()+(128/2-agk::GetTextTotalHeight(mana_level_text)/2));


//////////////////////////////////////////////////////////////////////////////
//                        END OF MANA PROCESSING                            //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                         ROCK SPAWNERS                                    //
//////////////////////////////////////////////////////////////////////////////

for(int n=0; n<rock_spawner_slot; n++)
{
//Check if current rock spawner can spawn a block already.
if(rock_spawner_delay[n] <= 0)
{
//Check for free rock slot.
int m=0;
while(agk::GetSpriteVisible(rock_sprite[n][m]) == 1 || m == 10)
{
m++;
}
if(m >= 10)
{
//Delete first block.
agk::SetSpriteVisible(rock_sprite[n][0],0);
agk::SetSpritePhysicsOff(rock_sprite[n][0]);
m = 0;
}
agk::SetSpriteVisible(rock_sprite[n][m],1);
agk::SetSpriteAngle(rock_sprite[n][m],rand()%360);
agk::SetSpriteShapeBox(rock_sprite[n][m],10,10,54,54);
agk::SetSpritePosition(rock_sprite[n][m],rock_spawner_x[n],rock_spawner_y[n]);
agk::SetSpriteColorAlpha(rock_sprite[n][m],255);
agk::SetSpritePhysicsOn(rock_sprite[n][m],2);
rock_live[n][m] = ROCK_LIVE;

//Set the delay again.
int delay = ROCK_SPAWN_DELAY;
rock_spawner_delay[n] = agk::Random(delay,delay+50);
} else {
//If not, decrease it's delay.
rock_spawner_delay[n]--;
}

}

/////////////////////
// PROCESS ROCKS   //
/////////////////////
for(int n=0; n<rock_spawner_slot; n++)
{
for(int m=0; m<10; m++)
{
if(agk::GetSpriteVisible(rock_sprite[n][m]))
{
if(rock_live[n][m] > 0)
{
rock_live[n][m]--;
agk::SetSpriteColorAlpha(rock_sprite[n][m],rock_live[n][m]);
if(agk::GetSpriteCollision(player_sprite,rock_sprite[n][m]) &&
agk::GetSpritePhysicsVelocityY(rock_sprite[n][m]) > 150)
{
//kill player.
//Set mana to full.
mana_bar->SetValue(50);
//Position our player correctly.
agk::SetSpritePhysicsOff(player_sprite);
agk::SetSpritePosition(player_sprite,start_x,start_y);
agk::SetSpritePhysicsOn(player_sprite,2);
//Decease all blocks.
for(int n=0; n<MAX_BLOCKS; n++)
{
agk::SetSpritePhysicsOff(magicblock[n]);
agk::SetSpriteVisible(magicblock[n],0);
agk::SetSpriteColorAlpha(magicblock[n],255);
magicblock_slot[n] = 0;
magicblock_live[n] = MAGICBLOCK_TIME;
}
picked_key = false;
agk::SetSpriteVisible(map_sprite[1][key_y][key_x],1);
}
} else {
//Delete the block.
agk::SetSpriteVisible(rock_sprite[n][m],0);
agk::SetSpritePhysicsOff(rock_sprite[n][m]);
agk::SetSpriteColorAlpha(rock_sprite[n][m],255);
rock_live[n][m] = ROCK_LIVE;
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
//                         END OF ROCK SPAWNERS                             //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//                         PROCESS RAYS                                     //
//////////////////////////////////////////////////////////////////////////////

// Make sure there ar any rays.
if(rays.rays_count > 0)
{
for(int n=0; n<rays.rays_count; n++)
{
//Check for type.
if(rays.type[n] == 1)
{
// Left ray
if(agk::GetSpriteX(player_sprite) < rays.x[n]+12)
agk::SetSpritePhysicsOff(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
else
agk::SetSpritePhysicsOn(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
}
if(rays.type[n] == 2)
{
// Right ray
if(agk::GetSpriteX(player_sprite) > rays.x[n]-12)
agk::SetSpritePhysicsOff(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
else
agk::SetSpritePhysicsOn(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
}
if(rays.type[n] == 3)
{
// Up ray
if(agk::GetSpriteY(player_sprite) < rays.y[n]+12)
agk::SetSpritePhysicsOff(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
else
agk::SetSpritePhysicsOn(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
}
if(rays.type[n] == 4)
{
// Down ray
if(agk::GetSpriteY(player_sprite) > rays.y[n]-12)
agk::SetSpritePhysicsOff(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
else
agk::SetSpritePhysicsOn(map_sprite[1][rays.y[n]/64][rays.x[n]/64]);
}
}
}
//////////////////////////////////////////////////////////////////////////////
//                         END OF PROCESSING RAYS                           //
//////////////////////////////////////////////////////////////////////////////

if(loading->state() == 1)
{
if(gamelevel != 30)
{
GameEnd();
gamelevel++;
//////////////
// SAVE     //
//////////////
agk::OpenToWrite(1,"spellbreath_save.save");
agk::WriteInteger(1,gamelevel);
agk::CloseFile(1);
w_location = "game";
} else {
GameEnd();
end_menu_call = true;
w_location = "menu";
agk::SetViewOffset(0,0);
}
}
if(loading->state() == 2)
{
//Just appeared.
}

//Process special events.
SGameLoop( gamelevel );
}

 Facepalm
Logged

Moczan
Level 5
*****



View Profile
« Reply #8 on: June 12, 2012, 05:34:46 AM »

And now we know why rate of finishing games is so low  Shrug
Logged
Halcyon
Level 2
**



View Profile
« Reply #9 on: June 12, 2012, 06:03:47 AM »

In my main.cpp file:

Code:
while(ENGINE.IsRunning())
{
    ENGINE.ProcessEvent();

    ENGINE.Update();

    ENGINE.Draw();

    ENGINE.ProcessDeltaTime();
}

To be fair my engine doesn't actually do all that much yet.  Wink
Logged
Dovuro
Level 0
*


View Profile
« Reply #10 on: June 12, 2012, 06:08:01 AM »

Nothing too fancy.

Code:
- (void)gameLoopThread
{
    STime time = STimeCurrent();
    
    for(;;) {
        // If the game loop should be gated, wait for the gate to unlock,
        // then relock the gate behind us
        if(gateGameLoop) {
            SGateWait(gate);
            SGateLock(gate);
        }
        
        // If the thread should suspend, break out of the game loop
        if(shouldSuspend) {
            shouldSuspend = NO;
            break;
        }
        
        // Surround every iteration through the game loop with an
        // NSAutoreleasePool
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        
        // Compute how much time has passed since the last pass through the
        // game loop
        STime now = STimeCurrent();
        STime deltaTime = now - time;
        time = now;
        
        deltaTime = MIN(0.25, deltaTime);
        
        // Perform all game-specific logic
        [gameLoop tick:deltaTime];
        
        // Clear out the NSAutoreleasePool
        [pool release];
    }
    
    running = NO;
}

This is for an iOS/Mac game. In order to ensure that things remain as responsive as possible, I use three threads. The first thread is the main thread, where all events from the system get processed. The second thread is the game thread where this code (and all game code) is run. The third thread is a thread that does nothing but listen for vertical blank display sync events off a CADisplayLink. I keep the game off the main thread because many events (including, notably, Game Center events), can take a non-trivial amount of time to process. The CADisplayLink is on a dedicated thread so I can have confidence that the CADisplayLink callback will reliably be called right when the screen is ready to refresh.

The SGate functions in that code involve a kind of lock. An SGate can be either locked or unlocked. A call to SGateWait on an unlocked gate will return immediately. A call to SGateWait on a locked gate will block execution until the gate is unlocked from another thread. The CADisplayLink callback does one thing, and one thing only: it calls SGateUnlock.
Logged
zacaj
Level 3
***


void main()


View Profile WWW Email
« Reply #11 on: June 12, 2012, 06:59:17 AM »

Code:
while (!done)
{
curTime=getMS();
POINT pt;
RECT rect;
GetCursorPos( &pt );
GetWindowRect(videoProperties.hwnd, &rect );
if ( !PtInRect( &rect, pt ) )//mouse not in window
{
mousePosition[0]=-99999;
}
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
done=1;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#ifdef USE_RAWINPUT
for(int i=0;i<raw_mouse_count(),i<16;i++)
{
MouseMove *move=alloc(MouseMove);
move->rx=get_raw_mouse_x_delta(i);
move->ry=get_raw_mouse_y_delta(i);
if(move->rx!=0 || move->ry!=0)
{
mousePosition[i].x+=move->rx;
mousePosition[i].y+=move->ry;
move->x=mousePosition[i].x;
move->y=mousePosition[i].y;
move->frame=frame;
move->mouse=i;
move->type=MOUSEMOVE;
move->time=curTime;
events.push_back(move);
}
else
dealloc(move);
for(int j=0;j<512;j++)//mouse buttons
if(is_raw_mouse_button_pressed(i,j)!=mousePressed[i][j])
{
mousePressed[i][j]=is_raw_mouse_button_pressed(i,j);
mouseClick(i,j,is_raw_mouse_button_pressed(i,j),mousePosition[i].x,mousePosition[i].y);
}
}
#endif
for(uint i=0;i<processes.size();i++)
{
if(processes[i]->shouldUpdate)
processes[i]->update();
}
update();
printFXErrors();
glClearDepth(1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
for(uint i=0;i<processes.size();i++)
{
if(processes[i]->shouldDraw)
processes[i]->draw();
}
draw();
SwapBuffers(videoProperties.hdc);
for(uint i=0;i<events.size();i++)
{
if(events[i]->type==KEYPRESS)
{
KeyPress *key=(KeyPress*)events[i];

if(key->pressed==1)
{
keypressed[key->key]=0;
}
else
keyreleased[key->key]=0;
}
events.erase(events.begin()+i);
i--;
}
nFrame++;
}
Win32 API/OpenGL 3.3.  I have a separate main() for each OS, so it's not nearly as abstract as it could be.
Logged

My twitter: @zacaj_

Quote from: mcc
Well let's just take a look at this "getting started" page and see--
Quote
Download and install cmake
Noooooooo
ThemsAllTook
Moderator
Level 9
******


Alex Diener


View Profile WWW
« Reply #12 on: June 12, 2012, 07:54:29 AM »

I was going to post everything including setup, but now that I look, it's way more complex than I thought and split across several modules. Here's the main inner part of it, at least:

Code:
static void run(void * context) {
GameplayState * self = context;

if (self->playerDied) {
self->playerDeathTime++;

if (self->playerDeathTime >= DEATH_FADE_DURATION_FRAMES) {
self->gameStateModel->restartRoom(self->gameStateModel);
self->playerInputController->setPlayerModel(self->playerInputController, self->gameStateModel->playerModel);

self->view->playFadeEffect(self->view, 0x000000FF, 0x00000000, RESPAWN_FADE_DURATION);
self->playerDied = false;
AudioManager_startMusic("music/dungeon.ogg");
}
}

self->playerInputController->update(self->playerInputController);
self->gameStateController->update(self->gameStateController);
self->frameIndex++;
}

PlayerInputController handles all reading and interpretation of user input. GameStateController handles all game logic updates. The death stuff seems like it doesn't really belong...guess I just didn't have anywhere else to put it.

Here's a trimmed-down version of the setup:

Code:
static FixedIntervalRunLoop * runLoop;

#define TICK_INTERVAL (1.0f / 120.0f)

static void timerFunc(void * context) {
runLoop->run(runLoop);
}

// Target_init() is the main entry point for my game code; main() resides in OS-specific wrappers
void Target_init() {
// Shell_getCurrentTime() returns a monotonically-increasing time in seconds. Passed in here as a function pointer.
runLoop = FixedIntervalRunLoop_create(Shell_getCurrentTime, TICK_INTERVAL, gameplayState->run, NULL);
Shell_setTimer(TICK_INTERVAL, true, timerFunc, NULL);
Shell_mainLoop();
}

Here's the important part of FixedIntervalRunLoop (whole thing available here):

Code:
void FixedIntervalRunLoop_run(FixedIntervalRunLoop * self) {
double currentTime, interval;

if (self->paused) {
return;
}
currentTime = self->timeFunction();
interval = (currentTime - self->lastTime) + self->slop;

while (interval >= self->stepInterval) {
self->stepCallback(self->stepContext);
interval -= self->stepInterval;
}
self->slop = interval;
self->lastTime = currentTime;

AutoFreePool_empty();
}

Very interesting to read how other people do it!
Logged
ThemsAllTook
Moderator
Level 9
******


Alex Diener


View Profile WWW
« Reply #13 on: June 12, 2012, 12:02:59 PM »


Gave them all a more thorough read, and I'm curious about a few things... The biggest difference I see is in how much abstraction is used. The longer ones reveal quite a bit about the game, and embed a large amount of real game logic directly into the loop. I'm wondering if eigenbom and kamac are happy with their designs... I used to code in a similar way, but these days I try to keep everything tiny and properly compartmentalized.

blergh.. Waaagh!

I see loadScreenUpdateClock.restart(), fileSystemClock.Reset(), and clock.restart() in here. Whaaaat? Two different naming and casing conventions for the clock API? The one with Reset() is commented out, though, so maybe it's was changed since then. I'm assuming restart() resets the time to 0, so you actually have a minor bug here... your intervals will be longer than the values you're checking in the if statements above the restart() calls unless this somehow executes at the exact time when the clock hits that value. Probably not an issue for how you're using them, but good to be aware of it!

The ISystemBase pattern is pretty interesting. I've always just called update() directly on each thing that needs it in succession. Never thought about using a common interface and looping through them like that before. Then again, I usually don't have quite so many things to update...

I do all the crazyness about the timers to have a solid 60-fps game, no more and no less. With any other method you can't just be sure (ok, except for vsync, that is).

vsync certainly isn't a bad idea for this. Timers can be a bit flaky in some cases. What I usually like to do is to run my timers at a higher rate than I'm expecting to draw (usually 120hz) and let vsync take care of normalizing the time between frames for me.

It seems odd that your iOS API is different from Mac and Windows. Any particular reason for that? Or is there an iOSPlatform::loop(frameTime) function you just didn't show?

Not a lot to say really, it does what it does.

What's the language? I don't recognize it.

But I have the suckiest, un-organized loop anyway.

Oh my. That's...quite a beast. Seems like you have almost your entire game in here. I chuckled a bit at the parts where you were getting a sprite's image ID and comparing it to things like sheet_subimage[53]...I wouldn't have even thought to write a line like that without defining a constant for 53 (and the other numbers you use) to give them names so I'd know what the heck they're supposed to be and why they're the right things to check for. It also seems weird to me to read out what sounds like a purely visual attribute to use in game logic, but that's probably because I'm extremely rigorous in keeping my game logic and drawing code separate.

Those big banner comments seem like good places to slice up that monstrous function into smaller ones for those specific tasks.

Have you ever had to change the number of levels in the game? Having to find and replace all of those 30s seems like a big risk for introducing subtle bugs. A constant defined somewhere would make it a bit easier; counting the number of levels in your level list (assuming you have such a thing) would make it easier still.

Keep them coming, guys!
Logged
Noah!
Level 6
*



View Profile WWW
« Reply #14 on: June 12, 2012, 12:19:41 PM »

Code:
while (! G9::quit()) {
if (! InputManager::doYoThang(Display::getScreen())) G9::quit(true);
t.update();
int dt = t.dt();
if (realchange) {
G9::setdt(dt);
realchange = false;
}

ms_acc += dt;
if (ms_acc > 20) {
ms_acc = 20; // spiralguard :-)
}
bool uc = false; // update check!
while (ms_acc >= 10) { // 10 = 100 FPS
UltraTelly::getCurrentScreen()->update(10);
Yuri::update();
ms_acc -= 10;
uc = true;
changed = true;
}
if (uc) {
Display::getScreen()->clear();
UltraTelly::getCurrentScreen()->draw();
Display::getScreen()->display();
realchange = true;
}
}

(edited somewhat to remove some debug stuff, commented-out experiments, etc)
Logged

Pages: [1] 2 3 4
Print
Jump to:  

Theme orange-lt created by panic