Upload
robert-clewley
View
205
Download
0
Embed Size (px)
Citation preview
{CurlyBraces2015: h3p://curlybraces.rocks}
Take-aways
• Learn to love constraints • Don’t give up before analyzing • Performance can be possible in high level languages
• Hash tables are underused • Example in a game
• Sci-‐fi survival adventure • Roguelike-‐ish • Top-‐down 2D • Strategy • Puzzles
• EducaQonal applicaQon ? • Using embedded python interpreter in
virtual terminals to hack ship’s computers!
Game application
• Real Qme • Detailed ship simulaQon • Open world • Pure Python
• Real Qme • Detailed ship simulaQon • Open world • Pure Python
Python is not popularly considered to be a high performance language
… but I’m good at it, and I can quickly
build large systems with it
Python is not popularly considered to be a high performance language
… but that’s not always true, I’m good at Python, and I can quickly
build large systems with it
So how would I achieve my real Qme features?
So how would I achieve my real Qme features?
Let’s consider dynamic updates to visibility on
map
• Example: Player is looking East in a small room
… then strafes Southwards
Line of Sight / Field of View
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
LOS: Shadow casQng principle, Bresenham Line Algorithm
FOV cone
Line of Sight / Field of View
• LOS computaQon between all pairs of enQQes can scale like n2 in complexity – n is number of enQQes (e.g. Qles, objects)
• Usually performed in real Qme (every clock Qck) – brute force
• Not a big deal with GPUs and C++
• But I’m doing this single-‐threaded in pure python – besides, there’s more to compute for the simulaQon each Qck
Line of Sight / Field of View
• LOS computaQon between all pairs of enQQes can scale like n2 in complexity – n is number of enQQes (e.g. Qles)
• Usually performed in real Qme (every clock Qck) – brute force
• Not a big deal with GPUs and C++
• But I’m doing this single-‐threaded in pure Python – besides, there’s more to compute for the simulaQon each Qck
Learn to love constraints
• They are opportuniQes for creaQvity and novelty – Turn them into a feature, e.g. Silent Hill’s use of fog
h3p://www.oddpulse.com/19-‐awesome-‐facts-‐you-‐probably-‐didnt-‐know-‐about-‐of-‐famous-‐video-‐games/
Design simplifications
• Large square Qles for all floors
• N, S, E, W wall combinaQons only
• Discrete Qle-‐to-‐Qle player moQon only
• Cone of forward vision only
• Maximum visual range imposed
Opportunities
• Restricted FOV creates tension • Simple Qles create a retro game feel
• Design permits pre-‐computaQon of LOS • wurt?
• Replace FLOPs with data table lookups! – actually, a hash table
Opportunities
• Tension • Retro feel
• Design permits pre-‐computaQon of LOS • wurt?
• Replace FLOPs with data table lookups! – actually, a hash table
Opportunities
• Tension • Retro feel
• Design permits pre-‐computaQon of LOS • wurt?
• Replace FLOPs with data table lookups! – actually, a hash table
Time vs. Memory
Time vs. Memory
CPU FLOPs are costly
RAM use is cheap
Time vs. Memory
CPU FLOPs are costly
Opportunities
• Tension • Retro feel
• Design permits pre-‐computaQon of LOS • wurt?
• Replace FLOPs with cheap table lookups! – actually, from a hash table
Divide and Conquer
• Decompose all combinaQons of player vs. wall
• Pre-‐compute the LOS combos • Store them for lookup at runQme
Combinatorics
• Four possible player view direcQons • N, S, E, W
• 14 Qle wall possibiliQes for creaQng shadow: – 1 with no walls (irrelevant) – 4 with single walls – 4 with adjacent double walls – 2 with opposite double walls – 4 triple walls – 1 four walls (irrelevant, back wall not visible)
Big buts (no lie)
• Many possible room outlines on a map
• Many possible player locaQons on a map
• Even a small 50 x 50 map has 2500 x 4 = 10k possible player viewpoints
• … and walls are potenQally breakable/buildable • So don’t pre-‐compute on a whole map basis
Scalable, modular, general For the East facing direcQon:
For each of the 14 wall combos in Qle: For each Qle relaQve locaQon within max range: Compute Qles in “shadow” using trigonometry, etc. [EXPENSIVE] up to max range (e.g. 18 Qles) Add to nested hash table:
{ E: { E: { [] },
W: { [] }, N: { [] }, S: { [] }, EW: { [] }, NS: { [] }, < etc. >
} }
Scalable, modular, general For the East facing direcQon:
For each of the 14 wall combos in Qle: South+East For each Qle relaQve locaQon within max range: (+3, -‐1) Compute Qles in “shadow” using trigonometry, etc. [EXPENSIVE] up to max range (e.g. 18 Qles) Add to nested hash table:
{ E: { SE:
{ (3,-‐1): [ (4,-‐1), (5,-‐1), (3,-‐2), (4,-‐2), (5,-‐2), (4,-‐3), (5,-‐3), … ] }
} }
Scalable, modular, general For the East facing direcQon:
For each of the 14 wall combos in Qle: South+East For each Qle relaQve locaQon within max range: (+3, -‐2) Compute Qles in “shadow” using trigonometry, etc. [EXPENSIVE] up to max range (e.g. 18 Qles) Add to nested hash table:
{ E: { SE:
{ (3,-‐1) [ (4,-‐1), (5,-‐1), (3,-‐2), (4,-‐2), (5,-‐2), (4,-‐3), (5,-‐3), …], (3,-‐2): [ (4,-‐2), (3,-‐3), (4,-‐3), (5,-‐3), (4,-‐4), (5,-‐4), … ] }
} }
Solution with reusable parts
Several wall combos can REUSE shadows already computed at this posiQon Rotate East-‐facing hash table for other facing direcQons [FURTHER REUSE!] Serialize whole table (~26k lists) to file for reloading at game’s start { E: { SE:
{ (3,-‐1): [ (4,-‐1), (5,-‐1), (3,-‐2), (4,-‐2), (5,-‐2), (4,-‐3), (5,-‐3), … ], (3,-‐2): [ (4,-‐2), (3,-‐3), (4,-‐3), (5,-‐3), (4,-‐4), (5,-‐4), … ], < etc. > },
< etc. > }, N: { < etc. > }, W: { < etc. > }, S: { < etc. > }, }
Runtime usage Hash table lookup has O(1) 1me complexity.
Each Cme Cck, player faces some direcCon, v: Radiate outwards in square rings from player: (pre-‐computed relaQve posiQons) For each absolute Qle locaQon in ring ∩ iniQal FOV cone:
Find any walls here (O(1) hash table query, not O(n) list search) For each wall: Lookup its facing direcQon, w Calculate its relaQve posiQon to the viewer, rel_pos
New invisible rel posiQons = shadow_dict [v] [w] [rel_pos] Compute the absolute Qle posiQon for each of these Add these to the list of invisible locaQons if not found previously
Visible locaQons = invert the list of invisibles
Runtime usage
FOV cone determines iniQal feasible set of visible
Qles
Player sprite
Runtime usage
r = 1
These concentric rings are also pre-‐computed
Runtime usage
Find any walls = O(1) hash table lookup
Runtime usage
shadow_dict [E] [S] [(1,-‐1)]
Runtime usage
shadow_dict [E] [S] [(0,-‐1)]
Filtered by FOV
Runtime usage
shadow_dict [E] [S] [(-‐1,-‐1)]
Filtered by FOV
Runtime usage
r = 2
shadow_dict [E] [S] [(2,-‐1)]
Eliminate ring Qles that are
already not in possibly visible Qle set
Runtime usage
r = 3
SE wall combo! shadow_dict [E] [SE] [(3,-‐1)]
… and so on
Take-home messages
Don’t underesQmate high-‐level languages if you work with constraints wisely
DicConaries are unordered, and the map is a grid! Don’t use a data structure (2D array) just because it
mimics the “physical” structure it represents (grid)
VectorizaQon (inc. list comprehension) is fast
Take-home messages
Tim Peters*: “Code wri3en with Python dicQonaries is a gazillion Qmes faster than C.” (i.e. compared to using non-‐hash table data types in C) Raymond Heunger: “If you need a [dicQonary] but try something else, it will be dog slow no ma3er what language you use.”
Sources: * Quoted by Raymond Heunger, at least h3ps://www.youtube.com/watch?v=hYUsssClE94 h3ps://wiki.python.org/moin/TimeComplexity h3p://www.laurentluce.com/posts/python-‐dicQonary-‐implementaQon/
(Hash tables are called “dicQonaries” in Python, a.k.a. “mappings”)