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

Login with username, password and session length

 
Advanced search

878040 Posts in 32903 Topics- by 24325 Members - Latest Member: hoplite21

May 21, 2013, 03:48:09 AM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Post your game loop
Pages: 1 2 3 [4]
Print
Author Topic: Post your game loop  (Read 2265 times)
cliffski
Level 0
***



View Profile WWW
« Reply #45 on: June 15, 2012, 05:27:27 AM »

cliffski> Cool!

Are the D3D* names specific to windows or is it an abstraction layer over everything graphic?
As Gratious Space Battle is running on several platform, I assume the latest?

It's just the name I've given to the class that interfaces with directx, which makes it easy for my mac partner to just swap out that class with his opengl version.
Logged

www.positech.co.uk Maker of Democracy Kudos and Gratuitous Space Battles for the PC. owner of showmethegames.com.
Skomakar'n
Level 10
*****


Vąutah


View Profile WWW Email
« Reply #46 on: June 15, 2012, 11:41:57 PM »

This is a small game that will have taken about 2,5 weeks when done, so it doesn't have to be that clean a game loop. It isn't finished, by the way. I will start playing menu music when changing back to menu state, too, but there is no menu music in existence yet.

Also, pasting this messed up the tab width and indentation somehow, but whatever.

Code:
window->Clear((getState() == STATE_MENUS) ? sf::Color(100,100,255) : sf::Color::White);
getInputManager()->beginFrame();

// Check if the state has changed.
if (getState() != *oldState)
{
switch (getState())
{
case STATE_MENUS:
{
musicManager->get("musicGame")->Stop();
break;
}

case STATE_GAME:
{
musicManager->get("musicGame")->Play();
level->reset();
break;
}
}
}

// Store the old state.
*oldState = getState();

// Check current state.
switch (getState())
{
case STATE_MENUS:
{
screenMenus->update();
screenMenus->draw();
break;
}

case STATE_GAME:
{
// Handle pausing and unpausing.
if (getWindow()->threeFingersWerePressed() && !getLevel()->mayNotUnpause())
getTimeManager()->setPaused(!getTimeManager()->isPaused());

level->update();
level->draw();
break;
}
}

window->Display();
getTimeManager()->update();
getInputManager()->endFrame();
Logged

mak gam
Geisha Novia: Out now!
Bottoms Up!: Devlog

Royal Railway on Twitter.

Adam Emil
Netsu
Level 10
*****



View Profile WWW
« Reply #47 on: June 16, 2012, 01:27:18 AM »

Pretty straight forward: get loop time, run logic, get input, communicate with the networking thread, update FPS, render frame.

Code:
while(running)
{
last_time = new_time;
new_time = SDL_GetTicks();
seconds += (new_time - last_time)/1000.0f;
while(seconds >= 1/LPS)
{
program_logic();
running = input();

if (!CNetplay::is_server()) CNetplay::clientActions();
else CNetplay::processIncomingActions();
seconds = seconds - 1/LPS;
tick_counter++;
}
if (tick_counter >= LPS)
{
tick_counter = 0;
fps = fps_counter;
fps_counter = 0;
}
CDisplay::render_frame(world_render, hud_render, gui_render);
fps_counter++;
}
Logged

Evan Balster
Level 10
*****


dreaming close to metal


View Profile WWW Email
« Reply #48 on: June 16, 2012, 07:29:53 AM »

Netsu:  As I mentioned before, the order of input/logic/output can affect gameplay.  Output reflects logic; logic reflects input.

Thus, while this dataflow produces a minimal latency:
Clock, Input -> Logic -> Graphics

This one causes all player inputs to take visible effect a minimum of two frames after the button press:
Logic, Input, Clock, Graphics

The former game loop generally produces a much "tighter"-feeling game than the latter.

(The scheme in Netsu's post imposes a minimum of one frame of input latency.)


For anyone not understanding why, I'll explain by tracing a button press through this system.  Say we press "fire" and are expecting a muzzle flare.

Frame 0: Logic, Input, Clock, Graphics

The system detects our button press, but game logic has already run, so there is no effect.  Since the frame effectively ends and begins again when Clock performs a delay, we stop there.

Frame 1: Logic, Input, Clock, Graphics

Graphics, and then game logic are run.  Input probably doesn't affect graphics directly, so logic is the first thing which responds to the key-press.  The bullet is "fired" now, though we don't see it yet.  We collect input again, and then the clock delays a number of milliseconds, adding another frame to the tally.

Frame 2: Logic, Input, Clock, Graphics

Finally, after two frames we see our bullet.  Somehow the extra 1/15 second of latency makes it less satisfying to pull that trigger.

And recall how I said that was a minimum value.  The nature of game logic and graphics can add further frames of delay to this; for instance, say that rather than a muzzle flare our bullets visualize as bright lines drawn between their current and former positions.  But what if the bullet doesn't get to move on the frame in which it's created and thus no line is visible?  That elevates our latency to 100 milliseconds in a 30 FPS game, or 50 in a 60 FPS game, when it could easily be on the order of the ~1-5 it takes to execute a single frame.


Anyway, this is just stuff everyone here should know so I thought I'd ramble.
Logged

Infinite Blank, SoundSelf, Cave Story+, Wreath
voice, accordion, mandolin, (oboe, soon)
Game audio programming consultant.
<plaid/audio>: opensource audio framework
Netsu
Level 10
*****



View Profile WWW
« Reply #49 on: June 16, 2012, 09:59:29 AM »

Thanks, haven't really thought about it before, I'll try updating logic after the input (and probably also after the networking, as it can work as an input source too) Smiley
Logged

rivon
Level 10
*****



View Profile
« Reply #50 on: June 16, 2012, 10:27:41 AM »

Input -> Logic -> Drawing...
Logged
stef1a
Level 1
*


confused


View Profile
« Reply #51 on: June 16, 2012, 10:55:11 AM »

Code:
void loop() {
bool running = true;
update.start();

while (running) {
fps.start();
// detect and handle events
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) running = false;
else handler.handle(event);
}
game.update(update.get_time_elapsed());
display.render();
// reset update timer
update.start();
display.refresh();
// delay to keep constant time/fps
if (fps.get_time_elapsed() < (1000/FPS)) {
SDL_Delay((1000/FPS) - fps.get_time_elapsed());
}
}
}

I'm quite happy with it.  Smiley
Logged
sublinimal
Level 6
*


This game is: UNPLAYABLE


View Profile
« Reply #52 on: June 16, 2012, 12:51:31 PM »

My current style of choice rotates around a pseudo-engine. The actual loop might be just...

Code: (Python)
frame = 0
while happen.process() and not key[K_ESCAPE]:
    frame += 1

...where happen is an instance of my EventHandler class, managing a stack of function/method pointers with game states, input, logic, graphics and so on.

A pointer added into the EventHandler is wrapped in an Event class containing the duration of the event (in frames), arguments for the function, a new event to trigger after the duration runs out, and of course a function for executing the event and reporting if it's active.

Code: (Python)
class EventHandler():
    def __init__(self):
        self.events = []

    def add_event(self, effect, duration=-1, args=None, followup=None, index=-1):
        new = Event(effect, duration, args, followup)
        self.events.insert(index, new)
        return new
   
    def process(self):
        x = 0
        while x < len(self.events):

            event = self.events[x]
            active = event.activate()
            consequence = event.followup is not None

            if not active:
                self.events.pop(x)
               
                if consequence:
                    self.add_event(event.followup, index=x)

            x += active or consequence

        return len(self.events)

class Event():
    def __init__(self, effect, duration, args, followup):
        self.act = effect
        self.time = duration
        self.args = args
        self.followup = followup
       
    def activate(self):
        if self.args is not None:
            if self.act(self.args)==0:
                return 0
        else:
            if self.act()==0:
                return 0

        self.time -= self.time>0
        return bool(self.time)

So it's a breeze to initialize a small demo and get it rolling with the main loop above.

Code: (Python)
def init():
    happen.add_event(clock.tick, args=60)            #Limits the speed to 60 FPS
    happen.add_event(get_key)                        #Sets the global 'key' dictionary for checking input
    happen.add_event(pygame.event.get)               #Ensures window focus
    happen.add_event(scr.fill, args=(112, 112, 190)) #Fills the screen

    #The order matters - first added objects are processed first
    #(Note: "Consequence" events are pushed at the "cause" event's position in the stack)
    for obj in [cloud, infobox1, player, bush]:
        #'act' being a polymorphic movement+draw method
        #If any of these guys return 0, infobox2 will pop up; but only infobox1 will, after Enter is pressed
        happen.add_event(obj.act, followup=infobox2.act)
       
    #At the end of the stack, update the screen and take a screenshot
    happen.add_event(pygame.display.update)
    happen.add_event(screenshot, duration=360)

The result:

Logged

Megaproject: Panacea
Big project: that other game
Control Room is dead, long live Veins of a Planet
Latest jam game: Vineyard Adagio (LD26)
danlthemanl
Level 0
***


The only thing I'm sure of, is that I don't know..

lostkeyboard42
View Profile WWW Email
« Reply #53 on: June 16, 2012, 04:17:45 PM »

Code:
public void update(GameContainer gc, int delta) throws SlickException {
cursorRect.setLocation(new Vector2f(cursorPos.x + 5,cursorPos.y + 5));

if (gc.getInput().isKeyPressed(Input.KEY_ESCAPE)) {
if (paused) {
gc.setMouseGrabbed(true);
paused = false;
} else if (!paused) {
gc.setMouseGrabbed(false);
paused = true;
}
}

if (!paused) {
for (Entity en : entities) {
en.update(gc, dt);
}
}
if(gc.getInput().isKeyPressed(Input.KEY_F1)) {
gc.exit();
}
}

I like to keep things simple.
Logged

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

Theme orange-lt created by panic