--- /dev/null
+! (c)2009 Joe Groff bsd license
+USING: help.markup help.syntax kernel math ui.gadgets.worlds ;
+IN: game.loop
+
+HELP: <game-loop>
+{ $values
+ { "tick-interval-micros" integer } { "delegate" "a " { $link "game.loop-delegates" } }
+ { "loop" game-loop }
+}
+{ $description "Constructs a new stopped " { $link game-loop } " object. When started, the game loop will call the " { $link tick* } " method on the " { $snippet "delegate" } " every " { $snippet "tick-interval-micros" } " microseconds, and " { $link draw* } " on the delegate as frequently as possible. The " { $link start-loop } " and " { $link stop-loop } " words start and stop the game loop." } ;
+
+HELP: benchmark-frames-per-second
+{ $values
+ { "loop" game-loop }
+ { "n" float }
+}
+{ $description "Returns the average number of times per second the game loop has called " { $link draw* } " on its delegate since the game loop was started with " { $link start-loop } " or since the benchmark counters have been reset with " { $link reset-loop-benchmark } "." } ;
+
+HELP: benchmark-ticks-per-second
+{ $values
+ { "loop" game-loop }
+ { "n" float }
+}
+{ $description "Returns the average number of times per second the game loop has called " { $link tick* } " on its delegate since the game loop was started with " { $link start-loop } " or since the benchmark counters have been reset with " { $link reset-loop-benchmark } "." } ;
+
+{ reset-loop-benchmark benchmark-frames-per-second benchmark-ticks-per-second } related-words
+
+HELP: draw*
+{ $values
+ { "tick-slice" float } { "delegate" "a " { $link "game.loop-delegates" } }
+}
+{ $description "This generic word is called by a " { $link game-loop } " on its " { $snippet "delegate" } " object in a tight loop while the game loop is running. The " { $snippet "tick-slice" } " value represents what fraction of the game loop's " { $snippet "tick-interval-micros" } " time period has passed since " { $link tick* } " was most recently called on the delegate." } ;
+
+HELP: game-loop
+{ $class-description "Objects of the " { $snippet "game-loop" } " class manage game loops. See " { $link "game.loop" } " for an overview of the game loop library. To construct a game loop, use " { $link <game-loop> } ". To start and stop a game loop, use the " { $link start-loop } " and " { $link stop-loop } " words." } ;
+
+HELP: game-loop-error
+{ $values
+ { "game-loop" game-loop } { "error" "an error object" }
+}
+{ $description "If an uncaught error is thrown from inside a game loop delegate's " { $link tick* } " or " { $link draw* } ", the game loop will catch the error, stop the game loop, and rethrow an error of this class." } ;
+
+HELP: reset-loop-benchmark
+{ $values
+ { "loop" game-loop }
+}
+{ $description "Resets the benchmark counters on a " { $link game-loop } ". Subsequent calls to " { $link benchmark-frames-per-second } " and " { $link benchmark-ticks-per-second } " will measure their values from the point " { $snippet "reset-loop-benchmark" } " was called." } ;
+
+HELP: start-loop
+{ $values
+ { "loop" game-loop }
+}
+{ $description "Starts running a " { $link game-loop } "." } ;
+
+HELP: stop-loop
+{ $values
+ { "loop" game-loop }
+}
+{ $description "Stops running a " { $link game-loop } "." } ;
+
+{ start-loop stop-loop } related-words
+
+HELP: tick*
+{ $values
+ { "delegate" "a " { $link "game.loop-delegates" } }
+}
+{ $description "This generic word is called by a " { $link game-loop } " on its " { $snippet "delegate" } " object at regular intervals while the game loop is running. The game loop's " { $snippet "tick-interval-micros" } " attribute determines the number of microseconds between invocations of " { $snippet "tick*" } "." } ;
+
+{ draw* tick* } related-words
+
+ARTICLE: "game.loop-delegates" "Game loop delegate"
+"A " { $link game-loop } " object requires a " { $snippet "delegate" } " that implements the logic that controls the game. A game loop delegate can be any object that provides two methods for the following generic words:"
+{ $subsections
+ tick*
+ draw*
+}
+{ $snippet "tick*" } " will be called at a regular interval determined by the game loop's " { $snippet "tick-interval-micros" } " attribute. " { $snippet "draw*" } " will be invoked in a tight loop, updating as frequently as possible." ;
+
+ARTICLE: "game.loop" "Game loops"
+"The " { $vocab-link "game.loop" } " vocabulary contains the implementation of a game loop. The game loop supports decoupled rendering and game logic timers; given a delegate object with methods on the " { $link tick* } " and " { $link draw* } " methods, the game loop will invoke the " { $snippet "tick*" } " method at regular intervals while invoking the " { $snippet "draw*" } " method as frequently as possible. Game loop objects must first be constructed:"
+{ $subsections
+ "game.loop-delegates"
+ <game-loop>
+}
+"Once constructed, the game loop can be started and stopped:"
+{ $subsections
+ start-loop
+ stop-loop
+}
+"The game loop maintains performance counters for measuring drawing frames and ticks per second:"
+{ $subsections
+ reset-loop-benchmark
+ benchmark-frames-per-second
+ benchmark-ticks-per-second
+}
+"The game loop manages errors that occur in the delegate's methods during the course of the game loop:"
+{ $subsections
+ game-loop-error
+}
+"The " { $vocab-link "game.worlds" } " vocabulary provides a convenient " { $link world } " subclass that integrates the game loop implementation with UI applications, managing the starting and stopping of the loop for you." ;
+
+ABOUT: "game.loop"
+! (c)2009 Joe Groff bsd license
USING: accessors calendar continuations destructors kernel math
math.order namespaces system threads ui ui.gadgets.worlds
sequences ;
--- /dev/null
+World class that integrates game loop and game input
--- /dev/null
+! (c)2009 Joe Groff bsd license
+USING: game.loop help.markup help.syntax kernel math method-chains
+ui ui.gadgets.worlds words ;
+IN: game.worlds
+
+HELP: GAME:
+{ $syntax """GAME: word { attributes }
+ attribute-code ;""" }
+{ $description "Similar to " { $link POSTPONE: MAIN-WINDOW: } ", defines a main entry point " { $snippet "word" } " for the current vocabulary that opens a UI window with the provided " { $snippet "attributes" } ". In addition to the standard " { $link world-attributes } ", additional " { $link game-attributes } " can be specified to specify game-specific attributes. Unlike " { $link POSTPONE: MAIN-WINDOW: } ", the " { $snippet "attributes" } " for " { $snippet "GAME:" } " must provide values for the " { $snippet "world-class" } " and " { $snippet "tick-interval-micros" } " slots." } ;
+
+HELP: game-attributes
+{ $class-description "Extends the " { $link world-attributes } " tuple class with extra attributes for " { $link game-world } "s:" }
+{ $list
+{ { $snippet "tick-interval-micros" } " specifies the number of microseconds between consecutive calls to the world's " { $link tick* } " method by the game loop." }
+} ;
+
+HELP: game-world
+{ $class-description "A subclass of " { $link world } " that automatically sets up and manages connections to the " { $vocab-link "game.loop" } " and " { $vocab-link "game.input" } " libraries. It does this by providing methods on " { $link begin-world } ", " { $link end-world } ", and " { $link draw* } ". Subclasses can provide their own world setup and teardown code by adding methods to the " { $link begin-game-world } " and " { $link end-game-world } " generic words." } ;
+
+HELP: begin-game-world
+{ $values { "world" game-world } }
+{ $description "This generic word is called by the " { $link begin-world } " method for " { $link game-world } " subclasses immediately before the game world starts the game loop." } ;
+
+HELP: end-game-world
+{ $values { "world" game-world } }
+{ $description "This generic word is called by the " { $link end-world } " method for " { $link game-world } " subclasses immediately after the game world stops the game loop." } ;
+
+{ game-world begin-game-world end-game-world } related-words
+
+HELP: tick-interval-micros
+{ $values
+ { "world" game-world }
+ { "micros" integer }
+}
+{ $description "Subclasses of " { $link game-world } " can override this class to specify the number of microseconds between consecutive calls to the game world's " { $link tick* } " method by the game loop. Using the " { $link POSTPONE: GAME: } " syntax will define this method for you." } ;
+
+ARTICLE: "game.worlds" "Game worlds"
+"The " { $vocab-link "game.worlds" } " vocabulary provides a " { $link world } " subclass that integrates with " { $vocab-link "game.loop" } " and " { $vocab-link "game.input" } " to quickly provide game infrastructure."
+{ $subsections
+ game-world
+ game-attributes
+ POSTPONE: GAME:
+}
+"Subclasses of " { $link game-world } " can provide their own setup and teardown code by providing methods for these generic words:"
+{ $subsections
+ begin-game-world
+ end-game-world
+} ;
+
+ABOUT: "game.worlds"
+! (c)2009 Joe Groff bsd license
USING: accessors combinators fry game.input game.loop generic kernel math
parser sequences ui ui.gadgets ui.gadgets.worlds ui.gestures threads
words ;
GENERIC: tick-interval-micros ( world -- micros )
+GENERIC: begin-game-world ( world -- )
+M: object begin-game-world drop ;
+
+GENERIC: end-game-world ( world -- )
+M: object end-game-world drop ;
+
M: game-world draw*
swap >>tick-slice relayout-1 yield ;
M: game-world begin-world
open-game-input
+ dup begin-game-world
dup [ tick-interval-micros ] [ ] bi <game-loop> [ >>game-loop ] keep start-loop
drop ;
M: game-world end-world
[ [ stop-loop ] when* f ] change-game-loop
- close-game-input
- drop ;
+ end-game-world
+ close-game-input ;
TUPLE: game-attributes < world-attributes
{ tick-interval-micros fixnum read-only } ;
+<PRIVATE
+
: verify-game-attributes ( attributes -- )
- world-class>> { f world } member?
- [ "GAME: must be given a custom world-class" throw ] when ;
+ {
+ [
+ world-class>> { f world } member?
+ [ "GAME: must be given a custom world-class" throw ] when
+ ]
+ [
+ tick-interval-micros>> 0 <=
+ [ "GAME: must be given a nonzero tick-interval-micros" throw ] when
+ ]
+ } cleave ;
: define-game-tick-interval-micros ( attributes -- )
[ world-class>> \ tick-interval-micros create-method ]
[ define-game-tick-interval-micros ]
} cleave ;
-: define-game ( word attributes -- )
- [ [ ] define-main-window ]
- [ nip define-game-methods ] 2bi ;
+: define-game ( word attributes quot -- )
+ [ define-main-window ]
+ [ drop nip define-game-methods ] 3bi ;
+
+PRIVATE>
SYNTAX: GAME:
CREATE
game-attributes parse-main-window-attributes
+ parse-definition
define-game ;
dup 0 "vocab:gpu/demos/bunny/loading.tiff" load-image allocate-texture-image
>>texture ;
-BEFORE: bunny-world begin-world
+M: bunny-world begin-game-world
init-gpu
{ -0.2 0.13 0.1 } 1.1 0.2 set-wasd-view
{ grab-input? t }
{ pref-dim { 1024 768 } }
{ tick-interval-micros $[ 1,000,000 60 /i ] }
- }
+ } ;
T{ sphere f { 1.0 0.0 0.0 } { 0.0 5.0 0.0 } 0.025 1.0 { 1.0 1.0 0.0 1.0 } }
}
-BEFORE: raytrace-world begin-world
+M: raytrace-world begin-game-world
init-gpu
{ -2.0 6.25 10.0 } 0.19 0.55 set-wasd-view
initial-spheres [ clone ] map >>spheres
{ grab-input? t }
{ pref-dim { 1024 768 } }
{ tick-interval-micros $[ 1,000,000 60 /i ] }
- }
+ } ;
--- /dev/null
+! (c)2009 Joe Groff bsd license
+USING: help.markup help.syntax ;
+IN: method-chains
+
+HELP: AFTER:
+{ $syntax "AFTER: class generic
+ implementation ;" }
+{ $description "Defines a method on " { $snippet "generic" } " for " { $snippet "class" } " which executes the new " { $snippet "implementation" } " code after invoking the parent class method on " { $snippet "generic" } "." } ;
+
+HELP: BEFORE:
+{ $syntax "BEFORE: class generic
+ implementation ;" }
+{ $description "Defines a method on " { $snippet "generic" } " for " { $snippet "class" } " which executes the new " { $snippet "implementation" } " code, then invokes the parent class method on " { $snippet "generic" } "." } ;
+
+ARTICLE: "method-chains" "Method chaining syntax"
+"The " { $vocab-link "method-chains" } " vocabulary provides syntax for extending method implementations in class hierarchies."
+{ $subsections
+ POSTPONE: AFTER:
+ POSTPONE: BEFORE:
+} ;
+
+ABOUT: "method-chains"
--- /dev/null
+BEFORE: and AFTER: syntax for extending methods in class hierarchies
opengl.shaders opengl.textures opengl.textures.private
sequences sequences.product specialized-arrays
terrain.generation terrain.shaders typed ui ui.gadgets
-ui.gadgets.worlds ui.pixel-formats game.worlds method-chains
+ui.gadgets.worlds ui.pixel-formats game.worlds
math.matrices.simd noise ui.gestures combinators.short-circuit
destructors grid-meshes math.vectors.simd ;
QUALIFIED-WITH: alien.c-types c
: sky-theta ( world -- theta )
game-loop>> tick-number>> SKY-SPEED * ;
-BEFORE: terrain-world begin-world
+M: terrain-world begin-game-world
"2.0" { "GL_ARB_vertex_buffer_object" "GL_ARB_shader_objects" }
require-gl-version-or-extensions
GL_DEPTH_TEST glEnable
terrain-vertex-size <grid-mesh> >>terrain-mesh
drop ;
-AFTER: terrain-world end-world
+M: terrain-world end-game-world
{
[ terrain-mesh>> dispose ]
[ terrain-program>> delete-gl-program ]
{ grab-input? t }
{ pref-dim { 1024 768 } }
{ tick-interval-micros $[ 1,000,000 60 /i ] }
- }
+ } ;