2. Agenda
Introduction to game scripting
•
Extending the game object model
•
State script syntax
•
Case studies from
•
Uncharted 2: Among Thieves
• Implementation discussion
• Summary and some tips
Game Developers Conference 2
4. Brief History of State Scripts
• Script system on Uncharted: Drake’s Fortune originally developed
for in-game cinematics (IGCs)
• Evolved into general gameplay scripting system
Game Developers Conference 4
5. Brief History of State Scripts
• UDF IGC system revamped for heavy
use on Uncharted 2: Among Thieves
• Inspirations:
GOAL language used on Crash and Jak series
State objects from God of War engine
TM
Game Developers Conference 5
6. Why Script?
• Scripting languages used in games since Quake C
• Primary benefits of script:
Takes pressure off engineering team
Code becomes data—rapid iteration
Empowers content creators
Key enabler of mod community
Game Developers Conference 6
7. Why Script?
• Scripting languages used in games since Quake C
• Primary benefits of script: I feel
Takes pressure off engineering team empowered!
Code becomes data—rapid iteration
Empowers content creators
Key enabler of mod community
Game Developers Conference 6
8. Scripting Language Characteristics
• Two kinds of game scripting languages:
data definition languages
runtime languages
• Runtime scripting languages typically:
interpreted by virtual machine (VM)
simple and small—low overhead
accessible to designers and other “non-programmers”
powerful—one line of code = big impact
Game Developers Conference 7
9. Choice of Language
• At Naughty Dog, we make heavy use of both data definition
and runtime script
Both based on PLT Scheme (a Lisp variant)
• Key benefits of Lisp-like languages:
Easy to parse
Data def and runtime code can be freely intermixed
Powerful macro system—easy to define custom syntax
Naughty Dog has a rich Lisp heritage—comfortable
Game Developers Conference 8
10. Choice of Language
• Of course you don’t have to use Lisp!
• Data definition languages:
custom text format,
Excel comma-separated values (.csv),
XML, ...
• Runtime languages:
Python, Lua, Pawn (Small C), OCaml, F#, ...
• Many popular engines already provide a scripting language:
Quake C, UnrealScript, C# (XNA), ...
Game Developers Conference 9
11. Extending the Game Object Model
• Every game engine has some kind of game object model
Defines all object types in game world
Often (but not always) written in an object-oriented language
• Scripting language often used to extend the native object model
• Many ways to accomplish this...
Game Developers Conference 10
12. Game Object Model References
• Rob Fermier, “Creating a Data Driven Engine,” GDC, 2002
• Scott Bilas, “A Data-Driven Game Object System,” GDC, 2002
(http://www.drizzle.com/~scottb/gdc/game-objects.ppt)
• Alex Duran, “Building Object Systems: Features, Tradeoffs and
Pitfalls,” GDC, 2003
• Doug Church, “Object Systems,” presented at a game development
conference in Seoul, Korea, 2003
(http://chrishecker.com/images/6/6f/ObjSys.ppt)
• Jason Gregory, “Game Engine Architecture,” AK Peters, 2009
(http://gameenginebook.com)
Game Developers Conference 11
13. Unreal’s Approach
• UnrealScript tightly integrated with C++ object model
Single-root class hierarchy with some add-on components
Classes defined in UnrealScript (.uc)
C++ header file (.h) automatically generated
Implementation in C++ or entirely in UnrealScript
Pawn.h Actor
generate
Pawn.uc
...
Info Pawn Pickup
Pawn.cp
p
implement
...
Scout
implement
Game Developers Conference 12
14. Property-Centric / Componentized Designs
• Property-centric design used on Thief, Dungeon Siege, Age of
Mythology, Deus Ex 2 and others
Game object just a unique id (UID)
“Decorated” with various properties
(health, armor, weaponry, etc.)
Property encapsulates data + behavior
Armor: Light
Health: 200
Object Weapon: BFG
(UID)
AI: PlayerAlly
Weapon: Pistol
Game Developers Conference 13
15. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
...
SsProcess DynLight
...
Character
...
Game Developers Conference 14
16. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
Objects
host of add-on components that update
over time
Process
...
SsProcess DynLight
...
Character
...
Game Developers Conference 14
17. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components Objects with
transform
Process
...
SsProcess DynLight
...
Character
...
Game Developers Conference 14
18. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components Base class
for all
characters
Process
...
SsProcess DynLight
...
Character
...
Game Developers Conference 14
19. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components Simple
animating
Process object
...
SsProcess DynLight
...
Character
...
Game Developers Conference 14
20. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
...
SsProcess DynLight
...
Character
Manages a
...
State Script
instance
Game Developers Conference 14
21. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
...
SsProcess DynLight
...
Character
Manages a
...
chunk of
running
script code
Game Developers Conference 14
22. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
SsProcess
Game Developers Conference 14
23. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
SsProcess
SsInstance
Game Developers Conference 14
24. Uncharted Engine’s Object Model
• Uncharted class hierarchy:
relatively shallow, single-root
host of add-on components
Process
...
AnimContr
SsInstance DrawContr
Game Developers Conference 14
25. Charter
• World editor for
Uncharted is called
Charter
Place game objects
Edit object
properties
Control level
streaming
Game Developers Conference 15
26. Uncharted State Scripts
• State scripts similar in many respects to property-centric model...
adds finite state machine (FSM) support
not specifically tied to “properties”
coarser-grained (one script per object)
• More like scripted extension to existing entity type...
... or a “director” that orchestrates actions of other entities
Game Developers Conference 16
27. Anatomy of a State Script
• A state script is comprised of:
attributes
states
• States define object’s behavior via runtime script code:
response to events
natural behavior over time (update event)
transitional actions between states (begin/end events)
Game Developers Conference 17
28. Anatomy of a State Script
State Script 1
Variables
State Script 2
State A
On Update Variables
On Event1
State C
On Begin
State B
On Event4
On Begin
Game Developers Conference 18
29. Anatomy of a State Script
State Script 1
Variables Event1
State Script 2
State A
On Update Variables
On Event1
State C
On Begin
State B
On Event4
On Begin
Game Developers Conference 18
30. Anatomy of a State Script
State Script 1
Variables Event1
State Script 2
State A
On Update Variables
On Event1
State C
go to
On Begin
State State B
On Event4
On Begin
Game Developers Conference 18
31. Anatomy of a State Script
State Script 1
Variables Event1
State Script 2
State A
On Update Variables
On Event1
State C
go to
On Begin
State State B
Event4 On Event4
On Begin
Game Developers Conference 18
32. Instantiating a State Script
• Attached to a native (C++) game object: Game
Object
designers extend or modify native
C++ object type State Script 1
define entirely new object types
Game Developers Conference 19
33. Instantiating a State Script
• Attached to a native (C++) game object: Game
Object
designers extend or modify native
C++ object type State Script 1
define entirely new object types
• Attached to a trigger region:
convex volume Trigger
Region
detects enter, exit and occupancy
State Script 2
Game Developers Conference 19
34. Instantiating State Scripts
• Placed as stand-alone object:
SsProces
s
“director” orchestrates actions of
State Script 3
other objects (e.g. IGC)
(stand-alone)
Game Developers Conference 20
35. Instantiating State Scripts
• Placed as stand-alone object:
SsProces
s
“director” orchestrates actions of
State Script 3
other objects (e.g. IGC)
(stand-alone)
• Associated with a task:
Task A
task = check point
script manages associated task
Task B1 Task B2
orchestrates AI encounters
Task C
controls player objectives
State Script 4
Task D
Game Developers Conference 20
36. Instantiating State Scripts
• Placed as stand-alone object:
SsProces
s
“director” orchestrates actions of
State Script 3
other objects (e.g. IGC)
(stand-alone)
• Associated with a task:
Task A
task = check point
script manages associated task
Task B1 Task B2
orchestrates AI encounters
Task C
controls player objectives
State Script 4
• Spawned by another state script
Task D
Game Developers Conference 20
39. State Script Syntax
• State script defined as follows:
Don’t let Lisp syntax throw you!
Lisp syntax is all (parentheses)
(define-state-script (quot;kickable-gatequot;)
Think C/C++ { } blocks (state (quot;lockedquot;)
• Parenthesized blocks highly ...
)
context-sensitive in Lisp/Scheme (state (quot;openingquot;)
...
)
(state (quot;openquot;)
...
)
)
Game Developers Conference 23
40. State Script Syntax
• State script defined as follows:
Don’t let Lisp syntax throw you!
Lisp syntax is all (parentheses)
(define-state-script (quot;kickable-gatequot;)
Think C/C++ { } blocks (state (quot;lockedquot;)
• Parenthesized blocks highly ...
)
context-sensitive in Lisp/Scheme (state (quot;openingquot;)
...
)
(state (quot;openquot;)
...
)
)
Game Developers Conference 23
41. State Script Syntax
• State script defined as follows:
Don’t let Lisp syntax throw you!
Lisp syntax is all (parentheses)
(define-state-script (quot;kickable-gatequot;)
Think C/C++ { } blocks (state (quot;lockedquot;)
• Parenthesized blocks highly ...
)
context-sensitive in Lisp/Scheme (state (quot;openingquot;)
...
)
(state (quot;openquot;)
...
)
)
Game Developers Conference 23
42. State Script Syntax
• State script defined as follows:
Don’t let Lisp syntax throw you!
Lisp syntax is all (parentheses)
(define-state-script (quot;kickable-gatequot;)
Think C/C++ { } blocks (state (quot;lockedquot;)
• Parenthesized blocks highly ...
)
context-sensitive in Lisp/Scheme (state (quot;openingquot;)
...
)
(state (quot;openquot;)
...
)
)
Game Developers Conference 23
43. Event Handler Blocks
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (event quot;kickquot;)
... ;; handle quot;kickquot; event
• Each state contains zero or more )
(on (begin)
event handler blocks ... ;; do when state entered
)
(on (update)
... ;; do every frame
)
(on (end)
... ;; do when state exited
)
)
Game Developers Conference 24
44. Event Handler Blocks
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (event quot;kickquot;)
... ;; handle quot;kickquot; event
• Each state contains zero or more )
(on (begin)
event handler blocks ... ;; do when state entered
)
(on (update)
... ;; do every frame
)
(on (end)
... ;; do when state exited
)
)
Game Developers Conference 24
45. Event Handler Blocks
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (event quot;kickquot;)
... ;; handle quot;kickquot; event
• Each state contains zero or more )
(on (begin)
event handler blocks ... ;; do when state entered
)
(on (update)
... ;; do every frame
)
(on (end)
... ;; do when state exited
)
)
Game Developers Conference 24
46. Event Handler Blocks
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (event quot;kickquot;)
... ;; handle quot;kickquot; event
• Each state contains zero or more )
(on (begin)
event handler blocks ... ;; do when state entered
)
(on (update)
... ;; do every frame
)
(on (end)
... ;; do when state exited
)
)
Game Developers Conference 24
47. Event Handler Blocks
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (event quot;kickquot;)
... ;; handle quot;kickquot; event
• Each state contains zero or more )
(on (begin)
event handler blocks ... ;; do when state entered
)
(on (update)
... ;; do every frame
)
(on (end)
... ;; do when state exited
)
)
Game Developers Conference 24
48. Runtime Script Code
• Runtime script code inside (on ...) (state (quot;lockedquot;)
(on (begin)
blocks [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
• Simplified Scheme/Lisp )
(on (event quot;kickedquot;)
Most “commands” are native calls [when [lock-broken?]
into C++ code [print-string quot;BAM!quot;]
]
Conditional expressions [if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
Simple looping via label/goto [print-string quot;NOT!!!quot;]
]
)
)
Game Developers Conference 25
49. Runtime Script Code
• Runtime script code inside (on ...) (state (quot;lockedquot;)
(on (begin)
blocks [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
• Simplified Scheme/Lisp )
(on (event quot;kickedquot;)
Most “commands” are native calls [when [lock-broken?]
into C++ code [print-string quot;BAM!quot;]
]
Conditional expressions [if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
Simple looping via label/goto [print-string quot;NOT!!!quot;]
]
)
)
Game Developers Conference 25
50. Runtime Script Code
• Runtime script code inside (on ...) (state (quot;lockedquot;)
(on (begin)
blocks [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
• Simplified Scheme/Lisp )
(on (event quot;kickedquot;)
Most “commands” are native calls [when [lock-broken?]
into C++ code [print-string quot;BAM!quot;]
]
Conditional expressions [if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
Simple looping via label/goto [print-string quot;NOT!!!quot;]
]
)
)
Game Developers Conference 25
51. Runtime Script Code
• Runtime script code inside (on ...) (state (quot;lockedquot;)
(on (begin)
blocks [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
• Simplified Scheme/Lisp )
(on (event quot;kickedquot;)
Most “commands” are native calls [when [lock-broken?]
into C++ code [print-string quot;BAM!quot;]
]
Conditional expressions [if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
Simple looping via label/goto [print-string quot;NOT!!!quot;]
]
)
)
Game Developers Conference 25
52. User-Defined Functions
(defun verbose-anim ((anim string))
[print-string quot;Starting quot; anim]
[animate quot;selfquot; anim]
)
• Script functions can be defined
and called by script programmer (define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (begin)
[verbose-anim quot;locked-idlequot;]
)
)
...
)
Game Developers Conference 26
53. User-Defined Functions
(defun verbose-anim ((anim string))
[print-string quot;Starting quot; anim]
[animate quot;selfquot; anim]
)
• Script functions can be defined
and called by script programmer (define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (begin)
[verbose-anim quot;locked-idlequot;]
)
)
...
)
Game Developers Conference 26
54. User-Defined Functions
(defun verbose-anim ((anim string))
[print-string quot;Starting quot; anim]
[animate quot;selfquot; anim]
)
• Script functions can be defined
and called by script programmer (define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (begin)
[verbose-anim quot;locked-idlequot;]
)
)
...
)
Game Developers Conference 26
55. User-Defined Functions
(defun verbose-anim ((anim string))
[print-string quot;Starting quot; anim]
[animate quot;selfquot; anim]
)
• Script functions can be defined
and called by script programmer (define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (begin)
[verbose-anim quot;locked-idlequot;]
)
)
...
)
Game Developers Conference 26
56. User-Defined Functions
(defun verbose-anim ((anim string))
[print-string quot;Starting quot; anim]
[animate quot;selfquot; anim]
)
• Script functions can be defined
and called by script programmer (define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
(on (begin)
[verbose-anim quot;locked-idlequot;]
)
)
...
)
Game Developers Conference 26
57. The “Self” Argument
• Any command operating on a game object takes its unique id
(UID) as its first argument
• Magic UID quot;selfquot; refers to the object to which script is attached
gate-17
Game
Object
State Script 1
Game Developers Conference 27
58. The “Self” Argument
• Any command operating on a game object takes its unique id
(UID) as its first argument
• Magic UID quot;selfquot; refers to the object to which script is attached
(on (begin)
gate-17
[animate quot;selfquot; quot;locked-idlequot;]
Game )
Object
State Script 1
self
Game Developers Conference 27
59. The “Self” Argument
• Any command operating on a game object takes its unique id
(UID) as its first argument
• Magic UID quot;selfquot; refers to the object to which script is attached
gate-17 (on (begin)
[animate quot;gate-17quot; quot;locked-idlequot;]
Game
)
Object
State Script 1
Game Developers Conference 27
60. The “Self” Argument
• Any command operating on a game object takes its unique id
(UID) as its first argument
• Magic UID quot;selfquot; refers to the object to which script is attached
gate-17
(on (begin)
Game [animate quot;lock-6quot; quot;fall-offquot;]
Object )
lock-6
State Script 1
Game
Object
Game Developers Conference 27
61. Changing States
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
• Transitions to other states via ...
(go quot;state-namequot;) command (on (event quot;kickedquot;)
[when [lock-broken?]
• State transitions cause current [go quot;openingquot;]
]
state’s (on ...) blocks to be )
)
aborted (state (quot;openingquot;)
(on (begin)
• Use (on (exit) ...) block for ...
clean-up )
...
)
)
Game Developers Conference 28
62. Changing States
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
• Transitions to other states via ...
(go quot;state-namequot;) command (on (event quot;kickedquot;)
[when [lock-broken?]
• State transitions cause current [go quot;openingquot;]
]
state’s (on ...) blocks to be )
)
aborted (state (quot;openingquot;)
(on (begin)
• Use (on (exit) ...) block for ...
clean-up )
...
)
)
Game Developers Conference 28
63. Changing States
(define-state-script (quot;kickable-gatequot;)
(state (quot;lockedquot;)
• Transitions to other states via ...
(go quot;state-namequot;) command (on (event quot;kickedquot;)
[when [lock-broken?]
• State transitions cause current [go quot;openingquot;]
]
state’s (on ...) blocks to be )
)
aborted (state (quot;openingquot;)
(on (begin)
• Use (on (exit) ...) block for ...
clean-up )
...
)
)
Game Developers Conference 28
64. Tracks
• (on ...) blocks contain one or more tracks
• A track is a bit like a thread or fiber
• Tracks can be put to sleep
Wait for duration
• e.g., wait 5 seconds,
• wait until frame 23, ...
Wait for an action to be done
• duration-agnostic
• Tracks can be synchronized via signals
Game Developers Conference 29
65. Tracks
State quot;shake-handsquot;
(state (quot;shake-handsquot;)
(on (begin) On Begin
(track (quot;playerquot;))
Track Track
[wait-move-to quot;playerquot; quot;waypoint7quot;]
P S
[signal quot;player-at-waypointquot;]
[wait-for-signal quot;sully-at-waypointquot;]
[wait-animate quot;playerquot; quot;shake-sullys-handquot;] s-a-w
)
(track (quot;sullivanquot;))
[wait-move-to quot;sullivanquot; quot;waypoint7quot;]
[signal quot;sully-at-waypointquot;] p-a-w
[wait-for-signal quot;player-at-waypointquot;]
[wait-animate quot;sullivanquot; quot;shake-drakes-handquot;]
)
)
)
Game Developers Conference 30
66. Tracks
State quot;shake-handsquot;
(state (quot;shake-handsquot;)
(on (begin) On Begin
(track (quot;playerquot;))
Track Track
[wait-move-to quot;playerquot; quot;waypoint7quot;]
P S
[signal quot;player-at-waypointquot;]
[wait-for-signal quot;sully-at-waypointquot;]
[wait-animate quot;playerquot; quot;shake-sullys-handquot;] s-a-w
)
(track (quot;sullivanquot;))
[wait-move-to quot;sullivanquot; quot;waypoint7quot;]
[signal quot;sully-at-waypointquot;] p-a-w
[wait-for-signal quot;player-at-waypointquot;]
[wait-animate quot;sullivanquot; quot;shake-drakes-handquot;]
)
)
)
Game Developers Conference 30
67. Tracks
State quot;shake-handsquot;
(state (quot;shake-handsquot;)
(on (begin) On Begin
(track (quot;playerquot;))
Track Track
[wait-move-to quot;playerquot; quot;waypoint7quot;]
P S
[signal quot;player-at-waypointquot;]
[wait-for-signal quot;sully-at-waypointquot;]
[wait-animate quot;playerquot; quot;shake-sullys-handquot;] s-a-w
)
(track (quot;sullivanquot;))
[wait-move-to quot;sullivanquot; quot;waypoint7quot;]
[signal quot;sully-at-waypointquot;] p-a-w
[wait-for-signal quot;player-at-waypointquot;]
[wait-animate quot;sullivanquot; quot;shake-drakes-handquot;]
)
)
)
Game Developers Conference 30
68. Tracks
State quot;shake-handsquot;
(state (quot;shake-handsquot;)
(on (begin) On Begin
(track (quot;playerquot;))
Track Track
[wait-move-to quot;playerquot; quot;waypoint7quot;]
P S
[signal quot;player-at-waypointquot;]
[wait-for-signal quot;sully-at-waypointquot;]
[wait-animate quot;playerquot; quot;shake-sullys-handquot;] s-a-w
)
(track (quot;sullivanquot;))
[wait-move-to quot;sullivanquot; quot;waypoint7quot;]
[signal quot;sully-at-waypointquot;] p-a-w
[wait-for-signal quot;player-at-waypointquot;]
[wait-animate quot;sullivanquot; quot;shake-drakes-handquot;]
)
)
)
Game Developers Conference 30
69. Tracks
State quot;shake-handsquot;
(state (quot;shake-handsquot;)
(on (begin) On Begin
(track (quot;playerquot;))
Track Track
[wait-move-to quot;playerquot; quot;waypoint7quot;]
P S
[signal quot;player-at-waypointquot;]
[wait-for-signal quot;sully-at-waypointquot;]
[wait-animate quot;playerquot; quot;shake-sullys-handquot;] p-a-w
)
(track (quot;sullivanquot;))
[wait-move-to quot;sullivanquot; quot;waypoint7quot;]
[signal quot;sully-at-waypointquot;] s-a-w
[wait-for-signal quot;player-at-waypointquot;]
[wait-animate quot;sullivanquot; quot;shake-drakes-handquot;]
)
)
)
Game Developers Conference 30
70. Track Execution Over Time
• Rules for execution of code within a track:
Greedily consume instructions sequentially...
... until a [wait* ...] command encountered...
... then relinquish control until the action is complete
• NOTE: A [wait* ...] command doesn’t have to wait; for example:
[wait-seconds 0]
[wait-npc-move-to quot;pos-4quot;] when she’s already there
Game Developers Conference 31
71. Options and Variable Declarations
• Options declared at top of
script:
(define-state-script (quot;kickable-gatequot;)
:initial-state quot;closedquot;
initial state
:declarations (decl-list
variable declarations
(var quot;num-attemptsquot; :type int32)
(var quot;is-lockedquot; :type boolean :default #t)
debugging options
)
(state (quot;kickedquot;)
...
)
...
)
Game Developers Conference 32
72. Options and Variable Declarations
• Options declared at top of
script:
(define-state-script (quot;kickable-gatequot;)
:initial-state quot;closedquot;
initial state
:declarations (decl-list
variable declarations
(var quot;num-attemptsquot; :type int32)
(var quot;is-lockedquot; :type boolean :default #t)
debugging options
)
(state (quot;kickedquot;)
...
)
...
)
Game Developers Conference 32
73. Options and Variable Declarations
• Options declared at top of
script:
(define-state-script (quot;kickable-gatequot;)
:initial-state quot;closedquot;
initial state
:declarations (decl-list
variable declarations
(var quot;num-attemptsquot; :type int32)
(var quot;is-lockedquot; :type boolean :default #t)
debugging options
)
(state (quot;kickedquot;)
...
)
...
)
Game Developers Conference 32
79. Configuration Parameters
• All game objects have
properties (key-value pairs)
Property values edited in
Charter
Game Developers Conference 34
80. Configuration Parameters
• All game objects have
properties (key-value pairs)
Property values edited in
Charter
Game Developers Conference 34
81. Configuration Parameters
• Designers can create their
own free-form properties,
called tags meaning-of-life = 42
Simply type “key = value”
in Tags field
Game Developers Conference 34
82. Configuration Parameters
• State scripts have read-only access to game object properties...
... and free-form tags
Game Developers Conference 35
83. Configuration Parameters
• State scripts have read-only access to game object properties...
... and free-form tags
(define-state-script (quot;kickable-gatequot;)
(state (quot;kickedquot;)
(on (begin)
[wait-animate quot;selfquot;
[tag-string quot;kick-animquot;]
]
)
...
)
Game Developers Conference 35
84. Configuration Parameters
• State scripts have read-only access to game object properties...
... and free-form tags
(define-state-script (quot;kickable-gatequot;)
(state (quot;kickedquot;)
(on (begin)
[wait-animate quot;selfquot;
[tag-string quot;kick-animquot;]
]
)
...
)
Game Developers Conference 35
85. Configuration Parameters
• State scripts have read-only access to game object properties...
... and free-form tags
(define-state-script (quot;kickable-gatequot;)
(state (quot;kickedquot;)
(on (begin)
[wait-animate quot;selfquot;
[tag-string quot;kick-animquot;]
]
)
...
)
Game Developers Conference 35
86. Data Definition and Runtime
(define-state-script (quot;kickable-gatequot;)
• Offline data-definition language (state (quot;lockedquot;)
(on (begin)
(Scheme) is intermingled with [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
runtime code )
(on (event quot;kickedquot;)
[when [lock-broken?]
[print-string quot;BAM!quot;]
]
[if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
[print-string quot;NOT!!!quot;]
]
)
)
...
)
Game Developers Conference 36
87. Data Definition and Runtime
(define-state-script (quot;kickable-gatequot;)
• Offline data-definition language (state (quot;lockedquot;)
(on (begin)
(Scheme) is intermingled with [print-string quot;Starting idle!quot;]
[animate quot;selfquot; quot;locked-idlequot;]
runtime code )
(on (event quot;kickedquot;)
[when [lock-broken?]
[print-string quot;BAM!quot;]
]
[if [task-complete? quot;wave2quot;]
[print-string quot;Complete!quot;]
[print-string quot;NOT!!!quot;]
]
)
)
...
)
Game Developers Conference 36
88. Data Definition and Runtime
class Vector3
{
private:
float x, y, z;
• Really no different than the public:
float Dot(const Vector3& b)
distinction between declarations {
return (x * b.x
and definitions in C++ + y * b.y
+ z * b.z);
}
...
};
Game Developers Conference 36
89. Data Definition and Runtime
class Vector3
{
private:
float x, y, z;
• Really no different than the public:
float Dot(const Vector3& b)
distinction between declarations {
return (x * b.x
and definitions in C++ + y * b.y
+ z * b.z);
}
...
};
Game Developers Conference 36
108. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
109. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
110. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
111. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
112. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
113. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
114. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
115. Making the Sign Script Generic
(state (quot;animatingquot;)
(on (begin)
[spawn-particles-at-joint quot;selfquot;
(define-state-script (quot;simple-animating-objquot;)
[tag-string quot;particle-jointquot;]
(state (quot;initialquot;)
[tag-string quot;particle-namequot;]]
(on (update)
[wait-animate quot;selfquot;
[when [task-complete?
[tag-string quot;anim-namequot;]]
[tag-string quot;done-taskquot;]]
[go quot;donequot;]
[go quot;donequot;]
)
]
)
)
(state (quot;donequot;)
(on (event quot;hanging-fromquot;)
(on (begin)
[go quot;animatingquot;]
[animate quot;selfquot;
)
[tag-string quot;done-anim-namequot;]]
)
)
...
)
)
Game Developers Conference 40
Notas del editor
Content creators gain an improved sense of ownership and empowerment.
Engineering team is often a bottleneck; expensive resource.
Treating code like data allows it to be modified and tested without requiring an engineer to recompile and relink the game.
Some engines support on the fly script reloading—don’t even need to rerun the game.
Data def languages allow data structures to be populated for consumption by engine.
Some data def languages even allow data structures to be defined by the scripter.
Runtime languages are typically:
Interpreted. Compiled into byte code, executed by a virtual machine (VM).
Simple and small. Easy to parse; virtual machine has minimal overhead.
Accessible. Designers, artists and other “non-programmers” can use it.
Powerful. Small amount of code = big impact on game.
We use PLT Scheme (a Lisp variant) for data definition (http://www.plt-scheme.org).
A simplified version of Scheme/Lisp (“Scheme lite”) is used for runtime scripting.
Runtime code is a special kind of lambda.
The game object model:
Defines the various object types that populate the game world.
(Player, NPC, weapon, power-up, light, trigger region, AI nav poly, camera, ...)
Often, but not always, implemented using an object-oriented language.
ore details, see:
Jason Gregory. Game Engine Architecture: A Comprehensive Guide. AK Peters, 2009.
“Lego block” approach to constructing new game object types
Components can be authored in native C++ or in script
The world editor for Uncharted is called Charter.
Place game objects, trigger regions, splines, etc.
Edit object properties.
Control level streaming.
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Uncharted task system defines player objectives, flow of control from “level” to “level”
Tasks are primary save-game mechanism
Task scripts are automatically killed when the task is completed
Square [brackets] can be used interchangeably with (parens)
Square [brackets] can be used interchangeably with (parens)
Square [brackets] can be used interchangeably with (parens)
Square [brackets] can be used interchangeably with (parens)
Square [brackets] can be used interchangeably with (parens)
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Events identified by free-form strings
Three special block types:
(on (begin) ...) Executed when state is first entered
(on (update) ...) Executed once every frame
(on (end) ...) Executed when state is exited
Runtime script code exists inside (on ...) blocks.
Runtime language is simplified Scheme:
Imperative style.
Most “commands” are native function calls into C++ code (hooks into the engine).
Language also supports conditional expressions. For example:
(when (condition) (do-if-true))
(if (condition) (do-if-true) (do-if-false))
Runtime script code exists inside (on ...) blocks.
Runtime language is simplified Scheme:
Imperative style.
Most “commands” are native function calls into C++ code (hooks into the engine).
Language also supports conditional expressions. For example:
(when (condition) (do-if-true))
(if (condition) (do-if-true) (do-if-false))
Runtime script code exists inside (on ...) blocks.
Runtime language is simplified Scheme:
Imperative style.
Most “commands” are native function calls into C++ code (hooks into the engine).
Language also supports conditional expressions. For example:
(when (condition) (do-if-true))
(if (condition) (do-if-true) (do-if-false))
Runtime script code exists inside (on ...) blocks.
Runtime language is simplified Scheme:
Imperative style.
Most “commands” are native function calls into C++ code (hooks into the engine).
Language also supports conditional expressions. For example:
(when (condition) (do-if-true))
(if (condition) (do-if-true) (do-if-false))
Runtime script code exists inside (on ...) blocks.
Runtime language is simplified Scheme:
Imperative style.
Most “commands” are native function calls into C++ code (hooks into the engine).
Language also supports conditional expressions. For example:
(when (condition) (do-if-true))
(if (condition) (do-if-true) (do-if-false))
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
Functions in Lisp are called “lambdas”
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
In Uncharted, object UIDs are 32-bit hashed strings called “string ids” Melds speed of an integer with human-readable string
“self” is much like the this pointer in C++
Very convenient, since many scripts are designed to control the object to which they are attached
Other magic UIDs include the names of singleton characters in the game (e.g. player, sullivan, elena, chloe, etc.)
(go ...) is really just a native C++ function call
(go ...) is really just a native C++ function call
(go ...) is really just a native C++ function call
(go ...) is really just a native C++ function call
(go ...) is really just a native C++ function call
(go ...) is really just a native C++ function call
Track syntax can be omitted when there’s only one track
Tracks are the only place runtime script code can exist
All tracks within an (on ...) block run in parallel
Ability to sleep is called “latent” code in Unreal
“Actions” include playing an animation, playing an audio cue, an NPC walking from point A to point B, etc.
Signals are like very simple Boolean semaphores