|
RobJinman
|
 |
« on: June 12, 2012, 01:00:38 AM » |
|
This thread is just for fun, but maybe we'll learn something  Post your game loop exactly as it appears in your code (I mean real code, not pseudo-code). Okay, here's mine: 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
|
 |
« Reply #1 on: June 12, 2012, 01:50:27 AM » |
|
I use too much high-level stuff, my main loops: 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 renderer.update(); on separate thread for AS3 project it's usually the same: currentState.update();
|
|
|
|
|
Logged
|
|
|
|
|
eigenbom
|
 |
« Reply #2 on: June 12, 2012, 02:03:59 AM » |
|
blergh..  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
|
 |
« Reply #3 on: June 12, 2012, 02:30:59 AM » |
|
I don't remember where I stole the frame limiter code from 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
|
 |
« 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.cppvoid Game::run() { RLC_LOG << "Starting " << GAME_TITLE ;
initialize();
m_running = true;
while( is_running() && display().IsOpened() ) { update(); render(); }
terminate(); } Then 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 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
|
|
|
|
|
_Tommo_
|
 |
« Reply #5 on: June 12, 2012, 04:42:45 AM » |
|
This may be a little different from the others: Engine start code, on Mac: 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: - (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 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. 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
|
 |
« Reply #6 on: June 12, 2012, 04:47:23 AM » |
|
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
|
|
|
|
|
kamac
|
 |
« Reply #7 on: June 12, 2012, 04:51:40 AM » |
|
eigenbom, that is alot of pointers  But I have the suckiest, un-organized loop anyway. 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 ); } 
|
|
|
|
|
Logged
|
|
|
|
|
Moczan
|
 |
« Reply #8 on: June 12, 2012, 05:34:46 AM » |
|
And now we know why rate of finishing games is so low 
|
|
|
|
|
Logged
|
|
|
|
|
Halcyon
|
 |
« Reply #9 on: June 12, 2012, 06:03:47 AM » |
|
In my main.cpp file: while(ENGINE.IsRunning()) { ENGINE.ProcessEvent();
ENGINE.Update();
ENGINE.Draw();
ENGINE.ProcessDeltaTime(); }
To be fair my engine doesn't actually do all that much yet. 
|
|
|
|
|
Logged
|
|
|
|
Dovuro
Level 0
|
 |
« Reply #10 on: June 12, 2012, 06:08:01 AM » |
|
Nothing too fancy. - (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
|
 |
« Reply #11 on: June 12, 2012, 06:59:17 AM » |
|
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_Well let's just take a look at this "getting started" page and see-- Download and install cmake
Noooooooo
|
|
|
|
ThemsAllTook
|
 |
« 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: 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: 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): 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
|
 |
« 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..  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!
|
 |
« Reply #14 on: June 12, 2012, 12:19:41 PM » |
|
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
|
|
|
|
|