├── .github └── FUNDING.yml ├── README.md ├── book.toml ├── book ├── .nojekyll ├── 0-capa.html ├── 01-quemsou.html ├── 404.html ├── FontAwesome │ ├── css │ │ └── font-awesome.css │ └── fonts │ │ ├── FontAwesome.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── ayu-highlight.css ├── book.js ├── clipboard.min.js ├── css │ ├── chrome.css │ ├── general.css │ ├── print.css │ └── variables.css ├── elasticlunr.min.js ├── favicon.png ├── favicon.svg ├── fonts │ ├── OPEN-SANS-LICENSE.txt │ ├── SOURCE-CODE-PRO-LICENSE.txt │ ├── fonts.css │ ├── open-sans-v17-all-charsets-300.woff2 │ ├── open-sans-v17-all-charsets-300italic.woff2 │ ├── open-sans-v17-all-charsets-600.woff2 │ ├── open-sans-v17-all-charsets-600italic.woff2 │ ├── open-sans-v17-all-charsets-700.woff2 │ ├── open-sans-v17-all-charsets-700italic.woff2 │ ├── open-sans-v17-all-charsets-800.woff2 │ ├── open-sans-v17-all-charsets-800italic.woff2 │ ├── open-sans-v17-all-charsets-italic.woff2 │ ├── open-sans-v17-all-charsets-regular.woff2 │ └── source-code-pro-v11-all-charsets-500.woff2 ├── highlight.css ├── highlight.js ├── imagens │ ├── .DS_Store │ ├── animation_time.jpg │ ├── interpolation.jpg │ ├── ping_time.jpg │ ├── reconciliacao.jpg │ ├── simpleblock.png │ ├── snake_pixel.png │ ├── step_time.jpg │ └── sync_problem.jpg ├── index.html ├── mark.min.js ├── part-1 │ ├── 00-capa.html │ ├── 01-arq.html │ ├── 02-prd-rec.html │ ├── 03-interpolacao.html │ └── 04-lag.html ├── part-2 │ ├── 00-intro.html │ ├── 01-bevy.html │ ├── 02-ecs.html │ ├── 03-cabeca.html │ ├── 04-grid.html │ ├── 05-spawnfood.html │ ├── 06-cadencia.html │ ├── 07-rabo.html │ ├── 08-colisoes.html │ ├── 09-migrando-versoes.html │ └── 10-multiplayer.html ├── print.html ├── searcher.js ├── searchindex.js ├── searchindex.json └── tomorrow-night.css ├── docs ├── .nojekyll ├── 0-capa.html ├── 01-quemsou.html ├── 404.html ├── FontAwesome │ ├── css │ │ └── font-awesome.css │ └── fonts │ │ ├── FontAwesome.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── ayu-highlight.css ├── book.js ├── clipboard.min.js ├── css │ ├── chrome.css │ ├── general.css │ ├── print.css │ └── variables.css ├── elasticlunr.min.js ├── favicon.png ├── favicon.svg ├── fonts │ ├── OPEN-SANS-LICENSE.txt │ ├── SOURCE-CODE-PRO-LICENSE.txt │ ├── fonts.css │ ├── open-sans-v17-all-charsets-300.woff2 │ ├── open-sans-v17-all-charsets-300italic.woff2 │ ├── open-sans-v17-all-charsets-600.woff2 │ ├── open-sans-v17-all-charsets-600italic.woff2 │ ├── open-sans-v17-all-charsets-700.woff2 │ ├── open-sans-v17-all-charsets-700italic.woff2 │ ├── open-sans-v17-all-charsets-800.woff2 │ ├── open-sans-v17-all-charsets-800italic.woff2 │ ├── open-sans-v17-all-charsets-italic.woff2 │ ├── open-sans-v17-all-charsets-regular.woff2 │ └── source-code-pro-v11-all-charsets-500.woff2 ├── highlight.css ├── highlight.js ├── imagens │ ├── .DS_Store │ ├── animation_time.jpg │ ├── interpolation.jpg │ ├── ping_time.jpg │ ├── reconciliacao.jpg │ ├── simpleblock.png │ ├── snake_pixel.png │ ├── step_time.jpg │ └── sync_problem.jpg ├── index.html ├── mark.min.js ├── part-1 │ ├── 00-capa.html │ ├── 01-arq.html │ ├── 02-prd-rec.html │ ├── 03-interpolacao.html │ └── 04-lag.html ├── part-2 │ ├── 00-intro.html │ ├── 01-bevy.html │ ├── 02-ecs.html │ ├── 03-cabeca.html │ ├── 04-grid.html │ ├── 05-spawnfood.html │ ├── 06-cadencia.html │ ├── 07-rabo.html │ ├── 08-colisoes.html │ ├── 09-migrando-versoes.html │ └── 10-multiplayer.html ├── print.html ├── searcher.js ├── searchindex.js ├── searchindex.json └── tomorrow-night.css └── src ├── 0-capa.md ├── 01-quemsou.md ├── SUMMARY.md ├── imagens ├── .DS_Store ├── animation_time.jpg ├── interpolation.jpg ├── ping_time.jpg ├── reconciliacao.jpg ├── simpleblock.png ├── snake_pixel.png ├── step_time.jpg └── sync_problem.jpg ├── part-1 ├── 00-capa.md ├── 01-arq.md ├── 02-prd-rec.md ├── 03-interpolacao.md └── 04-lag.md ├── part-2 ├── 00-intro.md ├── 01-bevy.md ├── 02-ecs.md ├── 03-cabeca.md ├── 04-grid.md ├── 05-spawnfood.md ├── 06-cadencia.md ├── 07-rabo.md ├── 08-colisoes.md ├── 09-migrando-versoes.md └── 10-multiplayer.md └── part-3 └── 00-capa.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 3 | custom: ["https://github.com/sponsors/naomijub", "https://patreon.com/naomijub"] 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Desenvolvimento de Jogos Online com Rust **[WIP]** 2 | 3 | Por Julia Naomi Boeira. 4 | 5 | [Github Sponsor](https://github.com/sponsors/naomijub) 6 | 7 | [](https://www.patreon.com/naomijub) 8 | [Patreon link](https://www.patreon.com/naomijub) 9 | 10 | [Livro](https://naomijub.github.io/Rust-game-dev) 11 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Julia Naomi "] 3 | language = "pt" 4 | multilingual = false 5 | src = "src" 6 | title = "Desenvolvimento de Jogos online com Rust" 7 | -------------------------------------------------------------------------------- /book/.nojekyll: -------------------------------------------------------------------------------- 1 | This file makes sure that Github Pages doesn't process mdBook's output. 2 | -------------------------------------------------------------------------------- /book/FontAwesome/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/FontAwesome/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /book/FontAwesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/FontAwesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /book/FontAwesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/FontAwesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /book/FontAwesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/FontAwesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /book/FontAwesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/FontAwesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /book/ayu-highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Based off of the Ayu theme 3 | Original by Dempfi (https://github.com/dempfi/ayu) 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | overflow-x: auto; 9 | background: #191f26; 10 | color: #e6e1cf; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #5c6773; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-variable, 20 | .hljs-template-variable, 21 | .hljs-attribute, 22 | .hljs-attr, 23 | .hljs-regexp, 24 | .hljs-link, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #ff7733; 28 | } 29 | 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-builtin-name, 33 | .hljs-literal, 34 | .hljs-type, 35 | .hljs-params { 36 | color: #ffee99; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-bullet { 41 | color: #b8cc52; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-built_in, 46 | .hljs-section { 47 | color: #ffb454; 48 | } 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-symbol { 53 | color: #ff7733; 54 | } 55 | 56 | .hljs-name { 57 | color: #36a3d9; 58 | } 59 | 60 | .hljs-tag { 61 | color: #00568d; 62 | } 63 | 64 | .hljs-emphasis { 65 | font-style: italic; 66 | } 67 | 68 | .hljs-strong { 69 | font-weight: bold; 70 | } 71 | 72 | .hljs-addition { 73 | color: #91b362; 74 | } 75 | 76 | .hljs-deletion { 77 | color: #d96c75; 78 | } 79 | -------------------------------------------------------------------------------- /book/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.4 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .buttons { 30 | z-index: 2; 31 | } 32 | 33 | a, a:visited, a:active, a:hover { 34 | color: #4183c4; 35 | text-decoration: none; 36 | } 37 | 38 | h1, h2, h3, h4, h5, h6 { 39 | page-break-inside: avoid; 40 | page-break-after: avoid; 41 | } 42 | 43 | pre, code { 44 | page-break-inside: avoid; 45 | white-space: pre-wrap; 46 | } 47 | 48 | .fa { 49 | display: none !important; 50 | } 51 | -------------------------------------------------------------------------------- /book/css/variables.css: -------------------------------------------------------------------------------- 1 | 2 | /* Globals */ 3 | 4 | :root { 5 | --sidebar-width: 300px; 6 | --sidebar-resize-indicator-width: 8px; 7 | --sidebar-resize-indicator-space: 2px; 8 | --page-padding: 15px; 9 | --content-max-width: 750px; 10 | --menu-bar-height: 50px; 11 | --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; 12 | --code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */ 13 | } 14 | 15 | /* Themes */ 16 | 17 | .ayu { 18 | --bg: hsl(210, 25%, 8%); 19 | --fg: #c5c5c5; 20 | 21 | --sidebar-bg: #14191f; 22 | --sidebar-fg: #c8c9db; 23 | --sidebar-non-existant: #5c6773; 24 | --sidebar-active: #ffb454; 25 | --sidebar-spacer: #2d334f; 26 | 27 | --scrollbar: var(--sidebar-fg); 28 | 29 | --icons: #737480; 30 | --icons-hover: #b7b9cc; 31 | 32 | --links: #0096cf; 33 | 34 | --inline-code-color: #ffb454; 35 | 36 | --theme-popup-bg: #14191f; 37 | --theme-popup-border: #5c6773; 38 | --theme-hover: #191f26; 39 | 40 | --quote-bg: hsl(226, 15%, 17%); 41 | --quote-border: hsl(226, 15%, 22%); 42 | 43 | --warning-border: #ff8e00; 44 | 45 | --table-border-color: hsl(210, 25%, 13%); 46 | --table-header-bg: hsl(210, 25%, 28%); 47 | --table-alternate-bg: hsl(210, 25%, 11%); 48 | 49 | --searchbar-border-color: #848484; 50 | --searchbar-bg: #424242; 51 | --searchbar-fg: #fff; 52 | --searchbar-shadow-color: #d4c89f; 53 | --searchresults-header-fg: #666; 54 | --searchresults-border-color: #888; 55 | --searchresults-li-bg: #252932; 56 | --search-mark-bg: #e3b171; 57 | 58 | --color-scheme: dark; 59 | } 60 | 61 | .coal { 62 | --bg: hsl(200, 7%, 8%); 63 | --fg: #98a3ad; 64 | 65 | --sidebar-bg: #292c2f; 66 | --sidebar-fg: #a1adb8; 67 | --sidebar-non-existant: #505254; 68 | --sidebar-active: #3473ad; 69 | --sidebar-spacer: #393939; 70 | 71 | --scrollbar: var(--sidebar-fg); 72 | 73 | --icons: #43484d; 74 | --icons-hover: #b3c0cc; 75 | 76 | --links: #2b79a2; 77 | 78 | --inline-code-color: #c5c8c6; 79 | 80 | --theme-popup-bg: #141617; 81 | --theme-popup-border: #43484d; 82 | --theme-hover: #1f2124; 83 | 84 | --quote-bg: hsl(234, 21%, 18%); 85 | --quote-border: hsl(234, 21%, 23%); 86 | 87 | --warning-border: #ff8e00; 88 | 89 | --table-border-color: hsl(200, 7%, 13%); 90 | --table-header-bg: hsl(200, 7%, 28%); 91 | --table-alternate-bg: hsl(200, 7%, 11%); 92 | 93 | --searchbar-border-color: #aaa; 94 | --searchbar-bg: #b7b7b7; 95 | --searchbar-fg: #000; 96 | --searchbar-shadow-color: #aaa; 97 | --searchresults-header-fg: #666; 98 | --searchresults-border-color: #98a3ad; 99 | --searchresults-li-bg: #2b2b2f; 100 | --search-mark-bg: #355c7d; 101 | 102 | --color-scheme: dark; 103 | } 104 | 105 | .light { 106 | --bg: hsl(0, 0%, 100%); 107 | --fg: hsl(0, 0%, 0%); 108 | 109 | --sidebar-bg: #fafafa; 110 | --sidebar-fg: hsl(0, 0%, 0%); 111 | --sidebar-non-existant: #aaaaaa; 112 | --sidebar-active: #1f1fff; 113 | --sidebar-spacer: #f4f4f4; 114 | 115 | --scrollbar: #8F8F8F; 116 | 117 | --icons: #747474; 118 | --icons-hover: #000000; 119 | 120 | --links: #20609f; 121 | 122 | --inline-code-color: #301900; 123 | 124 | --theme-popup-bg: #fafafa; 125 | --theme-popup-border: #cccccc; 126 | --theme-hover: #e6e6e6; 127 | 128 | --quote-bg: hsl(197, 37%, 96%); 129 | --quote-border: hsl(197, 37%, 91%); 130 | 131 | --warning-border: #ff8e00; 132 | 133 | --table-border-color: hsl(0, 0%, 95%); 134 | --table-header-bg: hsl(0, 0%, 80%); 135 | --table-alternate-bg: hsl(0, 0%, 97%); 136 | 137 | --searchbar-border-color: #aaa; 138 | --searchbar-bg: #fafafa; 139 | --searchbar-fg: #000; 140 | --searchbar-shadow-color: #aaa; 141 | --searchresults-header-fg: #666; 142 | --searchresults-border-color: #888; 143 | --searchresults-li-bg: #e4f2fe; 144 | --search-mark-bg: #a2cff5; 145 | 146 | --color-scheme: light; 147 | } 148 | 149 | .navy { 150 | --bg: hsl(226, 23%, 11%); 151 | --fg: #bcbdd0; 152 | 153 | --sidebar-bg: #282d3f; 154 | --sidebar-fg: #c8c9db; 155 | --sidebar-non-existant: #505274; 156 | --sidebar-active: #2b79a2; 157 | --sidebar-spacer: #2d334f; 158 | 159 | --scrollbar: var(--sidebar-fg); 160 | 161 | --icons: #737480; 162 | --icons-hover: #b7b9cc; 163 | 164 | --links: #2b79a2; 165 | 166 | --inline-code-color: #c5c8c6; 167 | 168 | --theme-popup-bg: #161923; 169 | --theme-popup-border: #737480; 170 | --theme-hover: #282e40; 171 | 172 | --quote-bg: hsl(226, 15%, 17%); 173 | --quote-border: hsl(226, 15%, 22%); 174 | 175 | --warning-border: #ff8e00; 176 | 177 | --table-border-color: hsl(226, 23%, 16%); 178 | --table-header-bg: hsl(226, 23%, 31%); 179 | --table-alternate-bg: hsl(226, 23%, 14%); 180 | 181 | --searchbar-border-color: #aaa; 182 | --searchbar-bg: #aeaec6; 183 | --searchbar-fg: #000; 184 | --searchbar-shadow-color: #aaa; 185 | --searchresults-header-fg: #5f5f71; 186 | --searchresults-border-color: #5c5c68; 187 | --searchresults-li-bg: #242430; 188 | --search-mark-bg: #a2cff5; 189 | 190 | --color-scheme: dark; 191 | } 192 | 193 | .rust { 194 | --bg: hsl(60, 9%, 87%); 195 | --fg: #262625; 196 | 197 | --sidebar-bg: #3b2e2a; 198 | --sidebar-fg: #c8c9db; 199 | --sidebar-non-existant: #505254; 200 | --sidebar-active: #e69f67; 201 | --sidebar-spacer: #45373a; 202 | 203 | --scrollbar: var(--sidebar-fg); 204 | 205 | --icons: #737480; 206 | --icons-hover: #262625; 207 | 208 | --links: #2b79a2; 209 | 210 | --inline-code-color: #6e6b5e; 211 | 212 | --theme-popup-bg: #e1e1db; 213 | --theme-popup-border: #b38f6b; 214 | --theme-hover: #99908a; 215 | 216 | --quote-bg: hsl(60, 5%, 75%); 217 | --quote-border: hsl(60, 5%, 70%); 218 | 219 | --warning-border: #ff8e00; 220 | 221 | --table-border-color: hsl(60, 9%, 82%); 222 | --table-header-bg: #b3a497; 223 | --table-alternate-bg: hsl(60, 9%, 84%); 224 | 225 | --searchbar-border-color: #aaa; 226 | --searchbar-bg: #fafafa; 227 | --searchbar-fg: #000; 228 | --searchbar-shadow-color: #aaa; 229 | --searchresults-header-fg: #666; 230 | --searchresults-border-color: #888; 231 | --searchresults-li-bg: #dec2a2; 232 | --search-mark-bg: #e69f67; 233 | 234 | --color-scheme: light; 235 | } 236 | 237 | @media (prefers-color-scheme: dark) { 238 | .light.no-js { 239 | --bg: hsl(200, 7%, 8%); 240 | --fg: #98a3ad; 241 | 242 | --sidebar-bg: #292c2f; 243 | --sidebar-fg: #a1adb8; 244 | --sidebar-non-existant: #505254; 245 | --sidebar-active: #3473ad; 246 | --sidebar-spacer: #393939; 247 | 248 | --scrollbar: var(--sidebar-fg); 249 | 250 | --icons: #43484d; 251 | --icons-hover: #b3c0cc; 252 | 253 | --links: #2b79a2; 254 | 255 | --inline-code-color: #c5c8c6; 256 | 257 | --theme-popup-bg: #141617; 258 | --theme-popup-border: #43484d; 259 | --theme-hover: #1f2124; 260 | 261 | --quote-bg: hsl(234, 21%, 18%); 262 | --quote-border: hsl(234, 21%, 23%); 263 | 264 | --warning-border: #ff8e00; 265 | 266 | --table-border-color: hsl(200, 7%, 13%); 267 | --table-header-bg: hsl(200, 7%, 28%); 268 | --table-alternate-bg: hsl(200, 7%, 11%); 269 | 270 | --searchbar-border-color: #aaa; 271 | --searchbar-bg: #b7b7b7; 272 | --searchbar-fg: #000; 273 | --searchbar-shadow-color: #aaa; 274 | --searchresults-header-fg: #666; 275 | --searchresults-border-color: #98a3ad; 276 | --searchresults-li-bg: #2b2b2f; 277 | --search-mark-bg: #355c7d; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /book/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/favicon.png -------------------------------------------------------------------------------- /book/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /book/fonts/OPEN-SANS-LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /book/fonts/SOURCE-CODE-PRO-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /book/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ 2 | /* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ 3 | 4 | /* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 5 | @font-face { 6 | font-family: 'Open Sans'; 7 | font-style: normal; 8 | font-weight: 300; 9 | src: local('Open Sans Light'), local('OpenSans-Light'), 10 | url('open-sans-v17-all-charsets-300.woff2') format('woff2'); 11 | } 12 | 13 | /* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 14 | @font-face { 15 | font-family: 'Open Sans'; 16 | font-style: italic; 17 | font-weight: 300; 18 | src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), 19 | url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); 20 | } 21 | 22 | /* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 23 | @font-face { 24 | font-family: 'Open Sans'; 25 | font-style: normal; 26 | font-weight: 400; 27 | src: local('Open Sans Regular'), local('OpenSans-Regular'), 28 | url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); 29 | } 30 | 31 | /* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 32 | @font-face { 33 | font-family: 'Open Sans'; 34 | font-style: italic; 35 | font-weight: 400; 36 | src: local('Open Sans Italic'), local('OpenSans-Italic'), 37 | url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); 38 | } 39 | 40 | /* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 41 | @font-face { 42 | font-family: 'Open Sans'; 43 | font-style: normal; 44 | font-weight: 600; 45 | src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), 46 | url('open-sans-v17-all-charsets-600.woff2') format('woff2'); 47 | } 48 | 49 | /* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 50 | @font-face { 51 | font-family: 'Open Sans'; 52 | font-style: italic; 53 | font-weight: 600; 54 | src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), 55 | url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); 56 | } 57 | 58 | /* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 59 | @font-face { 60 | font-family: 'Open Sans'; 61 | font-style: normal; 62 | font-weight: 700; 63 | src: local('Open Sans Bold'), local('OpenSans-Bold'), 64 | url('open-sans-v17-all-charsets-700.woff2') format('woff2'); 65 | } 66 | 67 | /* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 68 | @font-face { 69 | font-family: 'Open Sans'; 70 | font-style: italic; 71 | font-weight: 700; 72 | src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), 73 | url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); 74 | } 75 | 76 | /* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 77 | @font-face { 78 | font-family: 'Open Sans'; 79 | font-style: normal; 80 | font-weight: 800; 81 | src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), 82 | url('open-sans-v17-all-charsets-800.woff2') format('woff2'); 83 | } 84 | 85 | /* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 86 | @font-face { 87 | font-family: 'Open Sans'; 88 | font-style: italic; 89 | font-weight: 800; 90 | src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), 91 | url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); 92 | } 93 | 94 | /* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ 95 | @font-face { 96 | font-family: 'Source Code Pro'; 97 | font-style: normal; 98 | font-weight: 500; 99 | src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); 100 | } 101 | -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /book/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /book/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /book/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-tag, 20 | .hljs-name, 21 | .hljs-regexp, 22 | .hljs-link, 23 | .hljs-name, 24 | .hljs-selector-id, 25 | .hljs-selector-class { 26 | color: #d70025; 27 | } 28 | 29 | /* Orange */ 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-built_in, 33 | .hljs-builtin-name, 34 | .hljs-literal, 35 | .hljs-type, 36 | .hljs-params { 37 | color: #b21e00; 38 | } 39 | 40 | /* Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet { 44 | color: #008200; 45 | } 46 | 47 | /* Blue */ 48 | .hljs-title, 49 | .hljs-section { 50 | color: #0030f2; 51 | } 52 | 53 | /* Purple */ 54 | .hljs-keyword, 55 | .hljs-selector-tag { 56 | color: #9d00ec; 57 | } 58 | 59 | .hljs { 60 | display: block; 61 | overflow-x: auto; 62 | background: #f6f7f6; 63 | color: #000; 64 | } 65 | 66 | .hljs-emphasis { 67 | font-style: italic; 68 | } 69 | 70 | .hljs-strong { 71 | font-weight: bold; 72 | } 73 | 74 | .hljs-addition { 75 | color: #22863a; 76 | background-color: #f0fff4; 77 | } 78 | 79 | .hljs-deletion { 80 | color: #b31d28; 81 | background-color: #ffeef0; 82 | } 83 | -------------------------------------------------------------------------------- /book/imagens/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/.DS_Store -------------------------------------------------------------------------------- /book/imagens/animation_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/animation_time.jpg -------------------------------------------------------------------------------- /book/imagens/interpolation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/interpolation.jpg -------------------------------------------------------------------------------- /book/imagens/ping_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/ping_time.jpg -------------------------------------------------------------------------------- /book/imagens/reconciliacao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/reconciliacao.jpg -------------------------------------------------------------------------------- /book/imagens/simpleblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/simpleblock.png -------------------------------------------------------------------------------- /book/imagens/snake_pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/snake_pixel.png -------------------------------------------------------------------------------- /book/imagens/step_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/step_time.jpg -------------------------------------------------------------------------------- /book/imagens/sync_problem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/book/imagens/sync_problem.jpg -------------------------------------------------------------------------------- /book/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #969896; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-tag, 15 | .hljs-regexp, 16 | .ruby .hljs-constant, 17 | .xml .hljs-tag .hljs-title, 18 | .xml .hljs-pi, 19 | .xml .hljs-doctype, 20 | .html .hljs-doctype, 21 | .css .hljs-id, 22 | .css .hljs-class, 23 | .css .hljs-pseudo { 24 | color: #cc6666; 25 | } 26 | 27 | /* Tomorrow Orange */ 28 | .hljs-number, 29 | .hljs-preprocessor, 30 | .hljs-pragma, 31 | .hljs-built_in, 32 | .hljs-literal, 33 | .hljs-params, 34 | .hljs-constant { 35 | color: #de935f; 36 | } 37 | 38 | /* Tomorrow Yellow */ 39 | .ruby .hljs-class .hljs-title, 40 | .css .hljs-rule .hljs-attribute { 41 | color: #f0c674; 42 | } 43 | 44 | /* Tomorrow Green */ 45 | .hljs-string, 46 | .hljs-value, 47 | .hljs-inheritance, 48 | .hljs-header, 49 | .hljs-name, 50 | .ruby .hljs-symbol, 51 | .xml .hljs-cdata { 52 | color: #b5bd68; 53 | } 54 | 55 | /* Tomorrow Aqua */ 56 | .hljs-title, 57 | .css .hljs-hexcolor { 58 | color: #8abeb7; 59 | } 60 | 61 | /* Tomorrow Blue */ 62 | .hljs-function, 63 | .python .hljs-decorator, 64 | .python .hljs-title, 65 | .ruby .hljs-function .hljs-title, 66 | .ruby .hljs-title .hljs-keyword, 67 | .perl .hljs-sub, 68 | .javascript .hljs-title, 69 | .coffeescript .hljs-title { 70 | color: #81a2be; 71 | } 72 | 73 | /* Tomorrow Purple */ 74 | .hljs-keyword, 75 | .javascript .hljs-function { 76 | color: #b294bb; 77 | } 78 | 79 | .hljs { 80 | display: block; 81 | overflow-x: auto; 82 | background: #1d1f21; 83 | color: #c5c8c6; 84 | } 85 | 86 | .coffeescript .javascript, 87 | .javascript .xml, 88 | .tex .hljs-formula, 89 | .xml .javascript, 90 | .xml .vbscript, 91 | .xml .css, 92 | .xml .hljs-cdata { 93 | opacity: 0.5; 94 | } 95 | 96 | .hljs-addition { 97 | color: #718c00; 98 | } 99 | 100 | .hljs-deletion { 101 | color: #c82829; 102 | } 103 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | This file makes sure that Github Pages doesn't process mdBook's output. 2 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page not found - Desenvolvimento de Jogos online com Rust 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 58 | 59 | 60 | 70 | 71 | 72 | 82 | 83 | 84 | 85 | 1. Capa1.1. Quem sou eu? (Sobre a autora)2. Conceitos Básicos2.1. Arquitetura de servidores2.2. Predição e Reconciliação2.3. Interpolação de Entidades2.4. Compensacão de Lag3. Multiplayer Local Snake Game3.1. Sobre a Bevy3.2. Entity Component System3.3. A Cabeça da Cobra3.4. Grade de Movimento3.5. Gerador de Comidas3.6. Melhorando a Cadência do Movimento3.7. Adicionando um Rabo a Cobra3.8. Colisões3.9. Migrando versões da Bevy3.10. Multiplayer Local 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Light (default) 104 | Rust 105 | Coal 106 | Navy 107 | Ayu 108 | 109 | 110 | 111 | 112 | 113 | 114 | Desenvolvimento de Jogos online com Rust 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 143 | 144 | 145 | 146 | Document not found (404) 147 | This URL is invalid, sorry. Please use the navigation bar or search to continue. 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/FontAwesome/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/FontAwesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/FontAwesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/FontAwesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/FontAwesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/ayu-highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Based off of the Ayu theme 3 | Original by Dempfi (https://github.com/dempfi/ayu) 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | overflow-x: auto; 9 | background: #191f26; 10 | color: #e6e1cf; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #5c6773; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-variable, 20 | .hljs-template-variable, 21 | .hljs-attribute, 22 | .hljs-attr, 23 | .hljs-regexp, 24 | .hljs-link, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #ff7733; 28 | } 29 | 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-builtin-name, 33 | .hljs-literal, 34 | .hljs-type, 35 | .hljs-params { 36 | color: #ffee99; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-bullet { 41 | color: #b8cc52; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-built_in, 46 | .hljs-section { 47 | color: #ffb454; 48 | } 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-symbol { 53 | color: #ff7733; 54 | } 55 | 56 | .hljs-name { 57 | color: #36a3d9; 58 | } 59 | 60 | .hljs-tag { 61 | color: #00568d; 62 | } 63 | 64 | .hljs-emphasis { 65 | font-style: italic; 66 | } 67 | 68 | .hljs-strong { 69 | font-weight: bold; 70 | } 71 | 72 | .hljs-addition { 73 | color: #91b362; 74 | } 75 | 76 | .hljs-deletion { 77 | color: #d96c75; 78 | } 79 | -------------------------------------------------------------------------------- /docs/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.4 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .hljs { 18 | color: var(--links); 19 | } 20 | 21 | /* Menu Bar */ 22 | 23 | #menu-bar, 24 | #menu-bar-hover-placeholder { 25 | z-index: 101; 26 | margin: auto calc(0px - var(--page-padding)); 27 | } 28 | #menu-bar { 29 | position: relative; 30 | display: flex; 31 | flex-wrap: wrap; 32 | background-color: var(--bg); 33 | border-bottom-color: var(--bg); 34 | border-bottom-width: 1px; 35 | border-bottom-style: solid; 36 | } 37 | #menu-bar.sticky, 38 | .js #menu-bar-hover-placeholder:hover + #menu-bar, 39 | .js #menu-bar:hover, 40 | .js.sidebar-visible #menu-bar { 41 | position: -webkit-sticky; 42 | position: sticky; 43 | top: 0 !important; 44 | } 45 | #menu-bar-hover-placeholder { 46 | position: sticky; 47 | position: -webkit-sticky; 48 | top: 0; 49 | height: var(--menu-bar-height); 50 | } 51 | #menu-bar.bordered { 52 | border-bottom-color: var(--table-border-color); 53 | } 54 | #menu-bar i, #menu-bar .icon-button { 55 | position: relative; 56 | padding: 0 8px; 57 | z-index: 10; 58 | line-height: var(--menu-bar-height); 59 | cursor: pointer; 60 | transition: color 0.5s; 61 | } 62 | @media only screen and (max-width: 420px) { 63 | #menu-bar i, #menu-bar .icon-button { 64 | padding: 0 5px; 65 | } 66 | } 67 | 68 | .icon-button { 69 | border: none; 70 | background: none; 71 | padding: 0; 72 | color: inherit; 73 | } 74 | .icon-button i { 75 | margin: 0; 76 | } 77 | 78 | .right-buttons { 79 | margin: 0 15px; 80 | } 81 | .right-buttons a { 82 | text-decoration: none; 83 | } 84 | 85 | .left-buttons { 86 | display: flex; 87 | margin: 0 5px; 88 | } 89 | .no-js .left-buttons { 90 | display: none; 91 | } 92 | 93 | .menu-title { 94 | display: inline-block; 95 | font-weight: 200; 96 | font-size: 2.4rem; 97 | line-height: var(--menu-bar-height); 98 | text-align: center; 99 | margin: 0; 100 | flex: 1; 101 | white-space: nowrap; 102 | overflow: hidden; 103 | text-overflow: ellipsis; 104 | } 105 | .js .menu-title { 106 | cursor: pointer; 107 | } 108 | 109 | .menu-bar, 110 | .menu-bar:visited, 111 | .nav-chapters, 112 | .nav-chapters:visited, 113 | .mobile-nav-chapters, 114 | .mobile-nav-chapters:visited, 115 | .menu-bar .icon-button, 116 | .menu-bar a i { 117 | color: var(--icons); 118 | } 119 | 120 | .menu-bar i:hover, 121 | .menu-bar .icon-button:hover, 122 | .nav-chapters:hover, 123 | .mobile-nav-chapters i:hover { 124 | color: var(--icons-hover); 125 | } 126 | 127 | /* Nav Icons */ 128 | 129 | .nav-chapters { 130 | font-size: 2.5em; 131 | text-align: center; 132 | text-decoration: none; 133 | 134 | position: fixed; 135 | top: 0; 136 | bottom: 0; 137 | margin: 0; 138 | max-width: 150px; 139 | min-width: 90px; 140 | 141 | display: flex; 142 | justify-content: center; 143 | align-content: center; 144 | flex-direction: column; 145 | 146 | transition: color 0.5s, background-color 0.5s; 147 | } 148 | 149 | .nav-chapters:hover { 150 | text-decoration: none; 151 | background-color: var(--theme-hover); 152 | transition: background-color 0.15s, color 0.15s; 153 | } 154 | 155 | .nav-wrapper { 156 | margin-top: 50px; 157 | display: none; 158 | } 159 | 160 | .mobile-nav-chapters { 161 | font-size: 2.5em; 162 | text-align: center; 163 | text-decoration: none; 164 | width: 90px; 165 | border-radius: 5px; 166 | background-color: var(--sidebar-bg); 167 | } 168 | 169 | .previous { 170 | float: left; 171 | } 172 | 173 | .next { 174 | float: right; 175 | right: var(--page-padding); 176 | } 177 | 178 | @media only screen and (max-width: 1080px) { 179 | .nav-wide-wrapper { display: none; } 180 | .nav-wrapper { display: block; } 181 | } 182 | 183 | @media only screen and (max-width: 1380px) { 184 | .sidebar-visible .nav-wide-wrapper { display: none; } 185 | .sidebar-visible .nav-wrapper { display: block; } 186 | } 187 | 188 | /* Inline code */ 189 | 190 | :not(pre) > .hljs { 191 | display: inline; 192 | padding: 0.1em 0.3em; 193 | border-radius: 3px; 194 | } 195 | 196 | :not(pre):not(a) > .hljs { 197 | color: var(--inline-code-color); 198 | overflow-x: initial; 199 | } 200 | 201 | a:hover > .hljs { 202 | text-decoration: underline; 203 | } 204 | 205 | pre { 206 | position: relative; 207 | } 208 | pre > .buttons { 209 | position: absolute; 210 | z-index: 100; 211 | right: 0px; 212 | top: 2px; 213 | margin: 0px; 214 | padding: 2px 0px; 215 | 216 | color: var(--sidebar-fg); 217 | cursor: pointer; 218 | visibility: hidden; 219 | opacity: 0; 220 | transition: visibility 0.1s linear, opacity 0.1s linear; 221 | } 222 | pre:hover > .buttons { 223 | visibility: visible; 224 | opacity: 1 225 | } 226 | pre > .buttons :hover { 227 | color: var(--sidebar-active); 228 | border-color: var(--icons-hover); 229 | background-color: var(--theme-hover); 230 | } 231 | pre > .buttons i { 232 | margin-left: 8px; 233 | } 234 | pre > .buttons button { 235 | cursor: inherit; 236 | margin: 0px 5px; 237 | padding: 3px 5px; 238 | font-size: 14px; 239 | 240 | border-style: solid; 241 | border-width: 1px; 242 | border-radius: 4px; 243 | border-color: var(--icons); 244 | background-color: var(--theme-popup-bg); 245 | transition: 100ms; 246 | transition-property: color,border-color,background-color; 247 | color: var(--icons); 248 | } 249 | @media (pointer: coarse) { 250 | pre > .buttons button { 251 | /* On mobile, make it easier to tap buttons. */ 252 | padding: 0.3rem 1rem; 253 | } 254 | } 255 | pre > code { 256 | padding: 1rem; 257 | } 258 | 259 | /* FIXME: ACE editors overlap their buttons because ACE does absolute 260 | positioning within the code block which breaks padding. The only solution I 261 | can think of is to move the padding to the outer pre tag (or insert a div 262 | wrapper), but that would require fixing a whole bunch of CSS rules. 263 | */ 264 | .hljs.ace_editor { 265 | padding: 0rem 0rem; 266 | } 267 | 268 | pre > .result { 269 | margin-top: 10px; 270 | } 271 | 272 | /* Search */ 273 | 274 | #searchresults a { 275 | text-decoration: none; 276 | } 277 | 278 | mark { 279 | border-radius: 2px; 280 | padding: 0 3px 1px 3px; 281 | margin: 0 -3px -1px -3px; 282 | background-color: var(--search-mark-bg); 283 | transition: background-color 300ms linear; 284 | cursor: pointer; 285 | } 286 | 287 | mark.fade-out { 288 | background-color: rgba(0,0,0,0) !important; 289 | cursor: auto; 290 | } 291 | 292 | .searchbar-outer { 293 | margin-left: auto; 294 | margin-right: auto; 295 | max-width: var(--content-max-width); 296 | } 297 | 298 | #searchbar { 299 | width: 100%; 300 | margin: 5px auto 0px auto; 301 | padding: 10px 16px; 302 | transition: box-shadow 300ms ease-in-out; 303 | border: 1px solid var(--searchbar-border-color); 304 | border-radius: 3px; 305 | background-color: var(--searchbar-bg); 306 | color: var(--searchbar-fg); 307 | } 308 | #searchbar:focus, 309 | #searchbar.active { 310 | box-shadow: 0 0 3px var(--searchbar-shadow-color); 311 | } 312 | 313 | .searchresults-header { 314 | font-weight: bold; 315 | font-size: 1em; 316 | padding: 18px 0 0 5px; 317 | color: var(--searchresults-header-fg); 318 | } 319 | 320 | .searchresults-outer { 321 | margin-left: auto; 322 | margin-right: auto; 323 | max-width: var(--content-max-width); 324 | border-bottom: 1px dashed var(--searchresults-border-color); 325 | } 326 | 327 | ul#searchresults { 328 | list-style: none; 329 | padding-left: 20px; 330 | } 331 | ul#searchresults li { 332 | margin: 10px 0px; 333 | padding: 2px; 334 | border-radius: 2px; 335 | } 336 | ul#searchresults li.focus { 337 | background-color: var(--searchresults-li-bg); 338 | } 339 | ul#searchresults span.teaser { 340 | display: block; 341 | clear: both; 342 | margin: 5px 0 0 20px; 343 | font-size: 0.8em; 344 | } 345 | ul#searchresults span.teaser em { 346 | font-weight: bold; 347 | font-style: normal; 348 | } 349 | 350 | /* Sidebar */ 351 | 352 | .sidebar { 353 | position: fixed; 354 | left: 0; 355 | top: 0; 356 | bottom: 0; 357 | width: var(--sidebar-width); 358 | font-size: 0.875em; 359 | box-sizing: border-box; 360 | -webkit-overflow-scrolling: touch; 361 | overscroll-behavior-y: contain; 362 | background-color: var(--sidebar-bg); 363 | color: var(--sidebar-fg); 364 | } 365 | .sidebar-resizing { 366 | -moz-user-select: none; 367 | -webkit-user-select: none; 368 | -ms-user-select: none; 369 | user-select: none; 370 | } 371 | .js:not(.sidebar-resizing) .sidebar { 372 | transition: transform 0.3s; /* Animation: slide away */ 373 | } 374 | .sidebar code { 375 | line-height: 2em; 376 | } 377 | .sidebar .sidebar-scrollbox { 378 | overflow-y: auto; 379 | position: absolute; 380 | top: 0; 381 | bottom: 0; 382 | left: 0; 383 | right: 0; 384 | padding: 10px 10px; 385 | } 386 | .sidebar .sidebar-resize-handle { 387 | position: absolute; 388 | cursor: col-resize; 389 | width: 0; 390 | right: 0; 391 | top: 0; 392 | bottom: 0; 393 | } 394 | .js .sidebar .sidebar-resize-handle { 395 | cursor: col-resize; 396 | width: 5px; 397 | } 398 | .sidebar-hidden .sidebar { 399 | transform: translateX(calc(0px - var(--sidebar-width))); 400 | } 401 | .sidebar::-webkit-scrollbar { 402 | background: var(--sidebar-bg); 403 | } 404 | .sidebar::-webkit-scrollbar-thumb { 405 | background: var(--scrollbar); 406 | } 407 | 408 | .sidebar-visible .page-wrapper { 409 | transform: translateX(var(--sidebar-width)); 410 | } 411 | @media only screen and (min-width: 620px) { 412 | .sidebar-visible .page-wrapper { 413 | transform: none; 414 | margin-left: var(--sidebar-width); 415 | } 416 | } 417 | 418 | .chapter { 419 | list-style: none outside none; 420 | padding-left: 0; 421 | line-height: 2.2em; 422 | } 423 | 424 | .chapter ol { 425 | width: 100%; 426 | } 427 | 428 | .chapter li { 429 | display: flex; 430 | color: var(--sidebar-non-existant); 431 | } 432 | .chapter li a { 433 | display: block; 434 | padding: 0; 435 | text-decoration: none; 436 | color: var(--sidebar-fg); 437 | } 438 | 439 | .chapter li a:hover { 440 | color: var(--sidebar-active); 441 | } 442 | 443 | .chapter li a.active { 444 | color: var(--sidebar-active); 445 | } 446 | 447 | .chapter li > a.toggle { 448 | cursor: pointer; 449 | display: block; 450 | margin-left: auto; 451 | padding: 0 10px; 452 | user-select: none; 453 | opacity: 0.68; 454 | } 455 | 456 | .chapter li > a.toggle div { 457 | transition: transform 0.5s; 458 | } 459 | 460 | /* collapse the section */ 461 | .chapter li:not(.expanded) + li > ol { 462 | display: none; 463 | } 464 | 465 | .chapter li.chapter-item { 466 | line-height: 1.5em; 467 | margin-top: 0.6em; 468 | } 469 | 470 | .chapter li.expanded > a.toggle div { 471 | transform: rotate(90deg); 472 | } 473 | 474 | .spacer { 475 | width: 100%; 476 | height: 3px; 477 | margin: 5px 0px; 478 | } 479 | .chapter .spacer { 480 | background-color: var(--sidebar-spacer); 481 | } 482 | 483 | @media (-moz-touch-enabled: 1), (pointer: coarse) { 484 | .chapter li a { padding: 5px 0; } 485 | .spacer { margin: 10px 0; } 486 | } 487 | 488 | .section { 489 | list-style: none outside none; 490 | padding-left: 20px; 491 | line-height: 1.9em; 492 | } 493 | 494 | /* Theme Menu Popup */ 495 | 496 | .theme-popup { 497 | position: absolute; 498 | left: 10px; 499 | top: var(--menu-bar-height); 500 | z-index: 1000; 501 | border-radius: 4px; 502 | font-size: 0.7em; 503 | color: var(--fg); 504 | background: var(--theme-popup-bg); 505 | border: 1px solid var(--theme-popup-border); 506 | margin: 0; 507 | padding: 0; 508 | list-style: none; 509 | display: none; 510 | } 511 | .theme-popup .default { 512 | color: var(--icons); 513 | } 514 | .theme-popup .theme { 515 | width: 100%; 516 | border: 0; 517 | margin: 0; 518 | padding: 2px 10px; 519 | line-height: 25px; 520 | white-space: nowrap; 521 | text-align: left; 522 | cursor: pointer; 523 | color: inherit; 524 | background: inherit; 525 | font-size: inherit; 526 | } 527 | .theme-popup .theme:hover { 528 | background-color: var(--theme-hover); 529 | } 530 | .theme-popup .theme:hover:first-child, 531 | .theme-popup .theme:hover:last-child { 532 | border-top-left-radius: inherit; 533 | border-top-right-radius: inherit; 534 | } 535 | -------------------------------------------------------------------------------- /docs/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import 'variables.css'; 4 | 5 | :root { 6 | /* Browser default font-size is 16px, this way 1 rem = 10px */ 7 | font-size: 62.5%; 8 | } 9 | 10 | html { 11 | font-family: "Open Sans", sans-serif; 12 | color: var(--fg); 13 | background-color: var(--bg); 14 | text-size-adjust: none; 15 | -webkit-text-size-adjust: none; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | font-size: 1.6rem; 21 | overflow-x: hidden; 22 | } 23 | 24 | code { 25 | font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; 26 | font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ 27 | } 28 | 29 | /* make long words/inline code not x overflow */ 30 | main { 31 | overflow-wrap: break-word; 32 | } 33 | 34 | /* make wide tables scroll if they overflow */ 35 | .table-wrapper { 36 | overflow-x: auto; 37 | } 38 | 39 | /* Don't change font size in headers. */ 40 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 41 | font-size: unset; 42 | } 43 | 44 | .left { float: left; } 45 | .right { float: right; } 46 | .boring { opacity: 0.6; } 47 | .hide-boring .boring { display: none; } 48 | .hidden { display: none !important; } 49 | 50 | h2, h3 { margin-top: 2.5em; } 51 | h4, h5 { margin-top: 2em; } 52 | 53 | .header + .header h3, 54 | .header + .header h4, 55 | .header + .header h5 { 56 | margin-top: 1em; 57 | } 58 | 59 | h1:target::before, 60 | h2:target::before, 61 | h3:target::before, 62 | h4:target::before, 63 | h5:target::before, 64 | h6:target::before { 65 | display: inline-block; 66 | content: "»"; 67 | margin-left: -30px; 68 | width: 30px; 69 | } 70 | 71 | /* This is broken on Safari as of version 14, but is fixed 72 | in Safari Technology Preview 117 which I think will be Safari 14.2. 73 | https://bugs.webkit.org/show_bug.cgi?id=218076 74 | */ 75 | :target { 76 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); 77 | } 78 | 79 | .page { 80 | outline: 0; 81 | padding: 0 var(--page-padding); 82 | margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ 83 | } 84 | .page-wrapper { 85 | box-sizing: border-box; 86 | } 87 | .js:not(.sidebar-resizing) .page-wrapper { 88 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 89 | } 90 | 91 | .content { 92 | overflow-y: auto; 93 | padding: 0 5px 50px 5px; 94 | } 95 | .content main { 96 | margin-left: auto; 97 | margin-right: auto; 98 | max-width: var(--content-max-width); 99 | } 100 | .content p { line-height: 1.45em; } 101 | .content ol { line-height: 1.45em; } 102 | .content ul { line-height: 1.45em; } 103 | .content a { text-decoration: none; } 104 | .content a:hover { text-decoration: underline; } 105 | .content img, .content video { max-width: 100%; } 106 | .content .header:link, 107 | .content .header:visited { 108 | color: var(--fg); 109 | } 110 | .content .header:link, 111 | .content .header:visited:hover { 112 | text-decoration: none; 113 | } 114 | 115 | table { 116 | margin: 0 auto; 117 | border-collapse: collapse; 118 | } 119 | table td { 120 | padding: 3px 20px; 121 | border: 1px var(--table-border-color) solid; 122 | } 123 | table thead { 124 | background: var(--table-header-bg); 125 | } 126 | table thead td { 127 | font-weight: 700; 128 | border: none; 129 | } 130 | table thead th { 131 | padding: 3px 20px; 132 | } 133 | table thead tr { 134 | border: 1px var(--table-header-bg) solid; 135 | } 136 | /* Alternate background colors for rows */ 137 | table tbody tr:nth-child(2n) { 138 | background: var(--table-alternate-bg); 139 | } 140 | 141 | 142 | blockquote { 143 | margin: 20px 0; 144 | padding: 0 20px; 145 | color: var(--fg); 146 | background-color: var(--quote-bg); 147 | border-top: .1em solid var(--quote-border); 148 | border-bottom: .1em solid var(--quote-border); 149 | } 150 | 151 | 152 | :not(.footnote-definition) + .footnote-definition, 153 | .footnote-definition + :not(.footnote-definition) { 154 | margin-top: 2em; 155 | } 156 | .footnote-definition { 157 | font-size: 0.9em; 158 | margin: 0.5em 0; 159 | } 160 | .footnote-definition p { 161 | display: inline; 162 | } 163 | 164 | .tooltiptext { 165 | position: absolute; 166 | visibility: hidden; 167 | color: #fff; 168 | background-color: #333; 169 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 170 | left: -8px; /* Half of the width of the icon */ 171 | top: -35px; 172 | font-size: 0.8em; 173 | text-align: center; 174 | border-radius: 6px; 175 | padding: 5px 8px; 176 | margin: 5px; 177 | z-index: 1000; 178 | } 179 | .tooltipped .tooltiptext { 180 | visibility: visible; 181 | } 182 | 183 | .chapter li.part-title { 184 | color: var(--sidebar-fg); 185 | margin: 5px 0px; 186 | font-weight: bold; 187 | } 188 | 189 | .result-no-output { 190 | font-style: italic; 191 | } 192 | -------------------------------------------------------------------------------- /docs/css/print.css: -------------------------------------------------------------------------------- 1 | 2 | #sidebar, 3 | #menu-bar, 4 | .nav-chapters, 5 | .mobile-nav-chapters { 6 | display: none; 7 | } 8 | 9 | #page-wrapper.page-wrapper { 10 | transform: none; 11 | margin-left: 0px; 12 | overflow-y: initial; 13 | } 14 | 15 | #content { 16 | max-width: none; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .page { 22 | overflow-y: initial; 23 | } 24 | 25 | code { 26 | background-color: #666666; 27 | border-radius: 5px; 28 | 29 | /* Force background to be printed in Chrome */ 30 | -webkit-print-color-adjust: exact; 31 | } 32 | 33 | pre > .buttons { 34 | z-index: 2; 35 | } 36 | 37 | a, a:visited, a:active, a:hover { 38 | color: #4183c4; 39 | text-decoration: none; 40 | } 41 | 42 | h1, h2, h3, h4, h5, h6 { 43 | page-break-inside: avoid; 44 | page-break-after: avoid; 45 | } 46 | 47 | pre, code { 48 | page-break-inside: avoid; 49 | white-space: pre-wrap; 50 | } 51 | 52 | .fa { 53 | display: none !important; 54 | } 55 | -------------------------------------------------------------------------------- /docs/css/variables.css: -------------------------------------------------------------------------------- 1 | 2 | /* Globals */ 3 | 4 | :root { 5 | --sidebar-width: 300px; 6 | --page-padding: 15px; 7 | --content-max-width: 750px; 8 | --menu-bar-height: 50px; 9 | } 10 | 11 | /* Themes */ 12 | 13 | .ayu { 14 | --bg: hsl(210, 25%, 8%); 15 | --fg: #c5c5c5; 16 | 17 | --sidebar-bg: #14191f; 18 | --sidebar-fg: #c8c9db; 19 | --sidebar-non-existant: #5c6773; 20 | --sidebar-active: #ffb454; 21 | --sidebar-spacer: #2d334f; 22 | 23 | --scrollbar: var(--sidebar-fg); 24 | 25 | --icons: #737480; 26 | --icons-hover: #b7b9cc; 27 | 28 | --links: #0096cf; 29 | 30 | --inline-code-color: #ffb454; 31 | 32 | --theme-popup-bg: #14191f; 33 | --theme-popup-border: #5c6773; 34 | --theme-hover: #191f26; 35 | 36 | --quote-bg: hsl(226, 15%, 17%); 37 | --quote-border: hsl(226, 15%, 22%); 38 | 39 | --table-border-color: hsl(210, 25%, 13%); 40 | --table-header-bg: hsl(210, 25%, 28%); 41 | --table-alternate-bg: hsl(210, 25%, 11%); 42 | 43 | --searchbar-border-color: #848484; 44 | --searchbar-bg: #424242; 45 | --searchbar-fg: #fff; 46 | --searchbar-shadow-color: #d4c89f; 47 | --searchresults-header-fg: #666; 48 | --searchresults-border-color: #888; 49 | --searchresults-li-bg: #252932; 50 | --search-mark-bg: #e3b171; 51 | } 52 | 53 | .coal { 54 | --bg: hsl(200, 7%, 8%); 55 | --fg: #98a3ad; 56 | 57 | --sidebar-bg: #292c2f; 58 | --sidebar-fg: #a1adb8; 59 | --sidebar-non-existant: #505254; 60 | --sidebar-active: #3473ad; 61 | --sidebar-spacer: #393939; 62 | 63 | --scrollbar: var(--sidebar-fg); 64 | 65 | --icons: #43484d; 66 | --icons-hover: #b3c0cc; 67 | 68 | --links: #2b79a2; 69 | 70 | --inline-code-color: #c5c8c6; 71 | 72 | --theme-popup-bg: #141617; 73 | --theme-popup-border: #43484d; 74 | --theme-hover: #1f2124; 75 | 76 | --quote-bg: hsl(234, 21%, 18%); 77 | --quote-border: hsl(234, 21%, 23%); 78 | 79 | --table-border-color: hsl(200, 7%, 13%); 80 | --table-header-bg: hsl(200, 7%, 28%); 81 | --table-alternate-bg: hsl(200, 7%, 11%); 82 | 83 | --searchbar-border-color: #aaa; 84 | --searchbar-bg: #b7b7b7; 85 | --searchbar-fg: #000; 86 | --searchbar-shadow-color: #aaa; 87 | --searchresults-header-fg: #666; 88 | --searchresults-border-color: #98a3ad; 89 | --searchresults-li-bg: #2b2b2f; 90 | --search-mark-bg: #355c7d; 91 | } 92 | 93 | .light { 94 | --bg: hsl(0, 0%, 100%); 95 | --fg: hsl(0, 0%, 0%); 96 | 97 | --sidebar-bg: #fafafa; 98 | --sidebar-fg: hsl(0, 0%, 0%); 99 | --sidebar-non-existant: #aaaaaa; 100 | --sidebar-active: #1f1fff; 101 | --sidebar-spacer: #f4f4f4; 102 | 103 | --scrollbar: #8F8F8F; 104 | 105 | --icons: #747474; 106 | --icons-hover: #000000; 107 | 108 | --links: #20609f; 109 | 110 | --inline-code-color: #301900; 111 | 112 | --theme-popup-bg: #fafafa; 113 | --theme-popup-border: #cccccc; 114 | --theme-hover: #e6e6e6; 115 | 116 | --quote-bg: hsl(197, 37%, 96%); 117 | --quote-border: hsl(197, 37%, 91%); 118 | 119 | --table-border-color: hsl(0, 0%, 95%); 120 | --table-header-bg: hsl(0, 0%, 80%); 121 | --table-alternate-bg: hsl(0, 0%, 97%); 122 | 123 | --searchbar-border-color: #aaa; 124 | --searchbar-bg: #fafafa; 125 | --searchbar-fg: #000; 126 | --searchbar-shadow-color: #aaa; 127 | --searchresults-header-fg: #666; 128 | --searchresults-border-color: #888; 129 | --searchresults-li-bg: #e4f2fe; 130 | --search-mark-bg: #a2cff5; 131 | } 132 | 133 | .navy { 134 | --bg: hsl(226, 23%, 11%); 135 | --fg: #bcbdd0; 136 | 137 | --sidebar-bg: #282d3f; 138 | --sidebar-fg: #c8c9db; 139 | --sidebar-non-existant: #505274; 140 | --sidebar-active: #2b79a2; 141 | --sidebar-spacer: #2d334f; 142 | 143 | --scrollbar: var(--sidebar-fg); 144 | 145 | --icons: #737480; 146 | --icons-hover: #b7b9cc; 147 | 148 | --links: #2b79a2; 149 | 150 | --inline-code-color: #c5c8c6; 151 | 152 | --theme-popup-bg: #161923; 153 | --theme-popup-border: #737480; 154 | --theme-hover: #282e40; 155 | 156 | --quote-bg: hsl(226, 15%, 17%); 157 | --quote-border: hsl(226, 15%, 22%); 158 | 159 | --table-border-color: hsl(226, 23%, 16%); 160 | --table-header-bg: hsl(226, 23%, 31%); 161 | --table-alternate-bg: hsl(226, 23%, 14%); 162 | 163 | --searchbar-border-color: #aaa; 164 | --searchbar-bg: #aeaec6; 165 | --searchbar-fg: #000; 166 | --searchbar-shadow-color: #aaa; 167 | --searchresults-header-fg: #5f5f71; 168 | --searchresults-border-color: #5c5c68; 169 | --searchresults-li-bg: #242430; 170 | --search-mark-bg: #a2cff5; 171 | } 172 | 173 | .rust { 174 | --bg: hsl(60, 9%, 87%); 175 | --fg: #262625; 176 | 177 | --sidebar-bg: #3b2e2a; 178 | --sidebar-fg: #c8c9db; 179 | --sidebar-non-existant: #505254; 180 | --sidebar-active: #e69f67; 181 | --sidebar-spacer: #45373a; 182 | 183 | --scrollbar: var(--sidebar-fg); 184 | 185 | --icons: #737480; 186 | --icons-hover: #262625; 187 | 188 | --links: #2b79a2; 189 | 190 | --inline-code-color: #6e6b5e; 191 | 192 | --theme-popup-bg: #e1e1db; 193 | --theme-popup-border: #b38f6b; 194 | --theme-hover: #99908a; 195 | 196 | --quote-bg: hsl(60, 5%, 75%); 197 | --quote-border: hsl(60, 5%, 70%); 198 | 199 | --table-border-color: hsl(60, 9%, 82%); 200 | --table-header-bg: #b3a497; 201 | --table-alternate-bg: hsl(60, 9%, 84%); 202 | 203 | --searchbar-border-color: #aaa; 204 | --searchbar-bg: #fafafa; 205 | --searchbar-fg: #000; 206 | --searchbar-shadow-color: #aaa; 207 | --searchresults-header-fg: #666; 208 | --searchresults-border-color: #888; 209 | --searchresults-li-bg: #dec2a2; 210 | --search-mark-bg: #e69f67; 211 | } 212 | 213 | @media (prefers-color-scheme: dark) { 214 | .light.no-js { 215 | --bg: hsl(200, 7%, 8%); 216 | --fg: #98a3ad; 217 | 218 | --sidebar-bg: #292c2f; 219 | --sidebar-fg: #a1adb8; 220 | --sidebar-non-existant: #505254; 221 | --sidebar-active: #3473ad; 222 | --sidebar-spacer: #393939; 223 | 224 | --scrollbar: var(--sidebar-fg); 225 | 226 | --icons: #43484d; 227 | --icons-hover: #b3c0cc; 228 | 229 | --links: #2b79a2; 230 | 231 | --inline-code-color: #c5c8c6; 232 | 233 | --theme-popup-bg: #141617; 234 | --theme-popup-border: #43484d; 235 | --theme-hover: #1f2124; 236 | 237 | --quote-bg: hsl(234, 21%, 18%); 238 | --quote-border: hsl(234, 21%, 23%); 239 | 240 | --table-border-color: hsl(200, 7%, 13%); 241 | --table-header-bg: hsl(200, 7%, 28%); 242 | --table-alternate-bg: hsl(200, 7%, 11%); 243 | 244 | --searchbar-border-color: #aaa; 245 | --searchbar-bg: #b7b7b7; 246 | --searchbar-fg: #000; 247 | --searchbar-shadow-color: #aaa; 248 | --searchresults-header-fg: #666; 249 | --searchresults-border-color: #98a3ad; 250 | --searchresults-li-bg: #2b2b2f; 251 | --search-mark-bg: #355c7d; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/favicon.png -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/fonts/OPEN-SANS-LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/fonts/SOURCE-CODE-PRO-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ 2 | /* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ 3 | 4 | /* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 5 | @font-face { 6 | font-family: 'Open Sans'; 7 | font-style: normal; 8 | font-weight: 300; 9 | src: local('Open Sans Light'), local('OpenSans-Light'), 10 | url('open-sans-v17-all-charsets-300.woff2') format('woff2'); 11 | } 12 | 13 | /* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 14 | @font-face { 15 | font-family: 'Open Sans'; 16 | font-style: italic; 17 | font-weight: 300; 18 | src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), 19 | url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); 20 | } 21 | 22 | /* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 23 | @font-face { 24 | font-family: 'Open Sans'; 25 | font-style: normal; 26 | font-weight: 400; 27 | src: local('Open Sans Regular'), local('OpenSans-Regular'), 28 | url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); 29 | } 30 | 31 | /* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 32 | @font-face { 33 | font-family: 'Open Sans'; 34 | font-style: italic; 35 | font-weight: 400; 36 | src: local('Open Sans Italic'), local('OpenSans-Italic'), 37 | url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); 38 | } 39 | 40 | /* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 41 | @font-face { 42 | font-family: 'Open Sans'; 43 | font-style: normal; 44 | font-weight: 600; 45 | src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), 46 | url('open-sans-v17-all-charsets-600.woff2') format('woff2'); 47 | } 48 | 49 | /* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 50 | @font-face { 51 | font-family: 'Open Sans'; 52 | font-style: italic; 53 | font-weight: 600; 54 | src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), 55 | url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); 56 | } 57 | 58 | /* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 59 | @font-face { 60 | font-family: 'Open Sans'; 61 | font-style: normal; 62 | font-weight: 700; 63 | src: local('Open Sans Bold'), local('OpenSans-Bold'), 64 | url('open-sans-v17-all-charsets-700.woff2') format('woff2'); 65 | } 66 | 67 | /* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 68 | @font-face { 69 | font-family: 'Open Sans'; 70 | font-style: italic; 71 | font-weight: 700; 72 | src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), 73 | url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); 74 | } 75 | 76 | /* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 77 | @font-face { 78 | font-family: 'Open Sans'; 79 | font-style: normal; 80 | font-weight: 800; 81 | src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), 82 | url('open-sans-v17-all-charsets-800.woff2') format('woff2'); 83 | } 84 | 85 | /* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 86 | @font-face { 87 | font-family: 'Open Sans'; 88 | font-style: italic; 89 | font-weight: 800; 90 | src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), 91 | url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); 92 | } 93 | 94 | /* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ 95 | @font-face { 96 | font-family: 'Source Code Pro'; 97 | font-style: normal; 98 | font-weight: 500; 99 | src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); 100 | } 101 | -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /docs/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /docs/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-tag, 20 | .hljs-name, 21 | .hljs-regexp, 22 | .hljs-link, 23 | .hljs-name, 24 | .hljs-selector-id, 25 | .hljs-selector-class { 26 | color: #d70025; 27 | } 28 | 29 | /* Orange */ 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-built_in, 33 | .hljs-builtin-name, 34 | .hljs-literal, 35 | .hljs-type, 36 | .hljs-params { 37 | color: #b21e00; 38 | } 39 | 40 | /* Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet { 44 | color: #008200; 45 | } 46 | 47 | /* Blue */ 48 | .hljs-title, 49 | .hljs-section { 50 | color: #0030f2; 51 | } 52 | 53 | /* Purple */ 54 | .hljs-keyword, 55 | .hljs-selector-tag { 56 | color: #9d00ec; 57 | } 58 | 59 | .hljs { 60 | display: block; 61 | overflow-x: auto; 62 | background: #f6f7f6; 63 | color: #000; 64 | } 65 | 66 | .hljs-emphasis { 67 | font-style: italic; 68 | } 69 | 70 | .hljs-strong { 71 | font-weight: bold; 72 | } 73 | 74 | .hljs-addition { 75 | color: #22863a; 76 | background-color: #f0fff4; 77 | } 78 | 79 | .hljs-deletion { 80 | color: #b31d28; 81 | background-color: #ffeef0; 82 | } 83 | -------------------------------------------------------------------------------- /docs/imagens/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/.DS_Store -------------------------------------------------------------------------------- /docs/imagens/animation_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/animation_time.jpg -------------------------------------------------------------------------------- /docs/imagens/interpolation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/interpolation.jpg -------------------------------------------------------------------------------- /docs/imagens/ping_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/ping_time.jpg -------------------------------------------------------------------------------- /docs/imagens/reconciliacao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/reconciliacao.jpg -------------------------------------------------------------------------------- /docs/imagens/simpleblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/simpleblock.png -------------------------------------------------------------------------------- /docs/imagens/snake_pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/snake_pixel.png -------------------------------------------------------------------------------- /docs/imagens/step_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/step_time.jpg -------------------------------------------------------------------------------- /docs/imagens/sync_problem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/docs/imagens/sync_problem.jpg -------------------------------------------------------------------------------- /docs/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #969896; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-tag, 15 | .hljs-regexp, 16 | .ruby .hljs-constant, 17 | .xml .hljs-tag .hljs-title, 18 | .xml .hljs-pi, 19 | .xml .hljs-doctype, 20 | .html .hljs-doctype, 21 | .css .hljs-id, 22 | .css .hljs-class, 23 | .css .hljs-pseudo { 24 | color: #cc6666; 25 | } 26 | 27 | /* Tomorrow Orange */ 28 | .hljs-number, 29 | .hljs-preprocessor, 30 | .hljs-pragma, 31 | .hljs-built_in, 32 | .hljs-literal, 33 | .hljs-params, 34 | .hljs-constant { 35 | color: #de935f; 36 | } 37 | 38 | /* Tomorrow Yellow */ 39 | .ruby .hljs-class .hljs-title, 40 | .css .hljs-rule .hljs-attribute { 41 | color: #f0c674; 42 | } 43 | 44 | /* Tomorrow Green */ 45 | .hljs-string, 46 | .hljs-value, 47 | .hljs-inheritance, 48 | .hljs-header, 49 | .hljs-name, 50 | .ruby .hljs-symbol, 51 | .xml .hljs-cdata { 52 | color: #b5bd68; 53 | } 54 | 55 | /* Tomorrow Aqua */ 56 | .hljs-title, 57 | .css .hljs-hexcolor { 58 | color: #8abeb7; 59 | } 60 | 61 | /* Tomorrow Blue */ 62 | .hljs-function, 63 | .python .hljs-decorator, 64 | .python .hljs-title, 65 | .ruby .hljs-function .hljs-title, 66 | .ruby .hljs-title .hljs-keyword, 67 | .perl .hljs-sub, 68 | .javascript .hljs-title, 69 | .coffeescript .hljs-title { 70 | color: #81a2be; 71 | } 72 | 73 | /* Tomorrow Purple */ 74 | .hljs-keyword, 75 | .javascript .hljs-function { 76 | color: #b294bb; 77 | } 78 | 79 | .hljs { 80 | display: block; 81 | overflow-x: auto; 82 | background: #1d1f21; 83 | color: #c5c8c6; 84 | } 85 | 86 | .coffeescript .javascript, 87 | .javascript .xml, 88 | .tex .hljs-formula, 89 | .xml .javascript, 90 | .xml .vbscript, 91 | .xml .css, 92 | .xml .hljs-cdata { 93 | opacity: 0.5; 94 | } 95 | 96 | .hljs-addition { 97 | color: #718c00; 98 | } 99 | 100 | .hljs-deletion { 101 | color: #c82829; 102 | } 103 | -------------------------------------------------------------------------------- /src/0-capa.md: -------------------------------------------------------------------------------- 1 | # Desenvolvimento de Jogos Online com Rust 2 | 3 | Por Julia Naomi Boeira. 4 | 5 | 6 | [](https://www.patreon.com/naomijub) 7 | [Patreon link](https://www.patreon.com/naomijub) 8 | 9 | Escrever um livro open source é um trablho que precisa de incentivo e por isso Github Sponsor e Patreon são coisas importantes, pois além de atuarem como incetivo, são um bomr econhecimento do nosso trabalho. Escrevi bastantes livros pela casa do código, mas em especial no assunto Rust e Games eu sinto que falta alcance, e por isso gostaria de continuar produzindo esse tipo de material. 10 | 11 | ## Sobre o livro 12 | 13 | Até o momento planejei 3 partes para este livro: 14 | 1. [Conceitos Básicos](part-1/00-capa.md), onde vamos falar sobre como funciona um jogo online e quais são suas limitações. 15 | 2. Jogo da cobrinha com a engine Bevy, essa é a parte menos criativa do processo e é uma cópia traduzida deste tutorial [Bevy Snake Tutorial](https://mbuffett.com/posts/bevy-snake-tutorial/). A diferença é que vou adicionar elementos de um jogo multiplayer local. 16 | 3. Servidor autoritário com a Bevy. 17 | 18 | ESPERO QUE APROVEITEM A LEITURA e feedbacks são bem vindos. -------------------------------------------------------------------------------- /src/01-quemsou.md: -------------------------------------------------------------------------------- 1 | # Quem sou eu 2 | 3 | Eu sou uma desenolvedora de jogos na Ubisoft Winnipeg atuando no desenvolvimento de sistemas online, middlewares e ferramentas para jogos. Trabalho principalmente com C++, mas um pouco de C# e Rust. Sou autora dos livros: 4 | 5 | - 📖 [Lean Game Development - Inglês - Apress](https://www.apress.com/gp/book/9781484232156) 6 | - 📖 [Lean Game Development - Português - Casa do Código](https://www.casadocodigo.com.br/products/livro-lean-game-development) 7 | - 📖 [Programação Funcional e Concorrente em Rust - Casa do Código](https://www.casadocodigo.com.br/products/livro-rust-funcional-concorrente) 8 | - 📖 [TDD para Games - Casa do Código](https://www.casadocodigo.com.br/products/livro-tdd-games) 9 | - 📖 [[OPEN SOURCE] Desenvolvimento Web com Rust](https://github.com/naomijub/web-dev-rust-book) 10 | 11 | E atualmente estou desenvolvendo em paralelo a este livro o livro [Unity FPS game with TDD - Inglês](https://github.com/naomijub/Unity-FPS-game-with-TDD-Book). 12 | 13 | Tenho atuado como evangelista voluntaria de Rust desde 2017 quando me deparei com Rust pela primeira vez e percebi que esta maravilhosa linguagem era um raio de esperança nos problemas que eu tinha no desenvolvimento de jogos com C++. 14 | 15 | Hobbies como engenheira são aprender novas linguagens, em especial de paradigmas diferentes ou que pelo menos possuem uma forma bem diferente de resolver problemas, tornando Clojure e Elixir minhas duas outras linguagens favoritas. E no meu tempo livre escrever e fazer prototipos bobos de jogos como esses (quando eu estava aprendendo Java) https://github.com/naomijub/DiammondSeek e https://github.com/naomijub/PacmanLabyrinth. 16 | 17 | Curiosidade sobre aprender linguagens de programação, tentei aprender Java na faculdade, mas simplesmente não entrava na minha cabeça, foi graças a XNA e aos C# que consegui aprender Java e arrumar trabalho com software corporativo. Tentei aprender Go 3 vezes e NUNCA entra na minha cabeça. Trabalhei muito com Python a ponto de dizer que era uma das minhas linguagens favoritas, mas hoje em dia eu fujo de qualquer projeto Python. 18 | 19 | Passei pelas faculdades de Matematica Aplicada, Engenharia de Materiais e Ciências da Computação. Larguei a CC porque já estava cursando mestrado em inteligência artificial aplicada a engenharia de materiais e depois aiinda fiz especialização em desenvolvimento de jogos para suprir as áreas que faltavam do meu conhecimento em jogos. A empresa que mais gostei de trabalhar é a Ubiisoft DE WINNIPEG, mas outras empresas que gostei muito foram a Thoughtworks até 2018, onde conheci pessoas incríveis que são minhas amigas até hoje, e Nubank que foi um lugar de muito aprendizado. 20 | 21 | Para dúvidas sobre o livro, discussões sobre o tema e correções sugiro abrir [issues](https://github.com/naomijub/Rust-game-dev/issues) ou criar [Pull Requests](https://github.com/naomijub/Rust-game-dev). 22 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Capa](0-capa.md) 4 | - [Quem sou eu? (Sobre a autora)](01-quemsou.md) 5 | - [Conceitos Básicos](part-1/00-capa.md) 6 | - [Arquitetura de servidores](part-1/01-arq.md) 7 | - [Predição e Reconciliação](part-1/02-prd-rec.md) 8 | - [Interpolação de Entidades](part-1/03-interpolacao.md) 9 | - [Compensacão de Lag](part-1/04-lag.md) 10 | - [Multiplayer Local Snake Game](part-2/00-intro.md) 11 | - [Sobre a Bevy](part-2/01-bevy.md) 12 | - [Entity Component System](part-2/02-ecs.md) 13 | - [A Cabeça da Cobra](part-2/03-cabeca.md) 14 | - [Grade de Movimento](part-2/04-grid.md) 15 | - [Gerador de Comidas](part-2/05-spawnfood.md) 16 | - [Melhorando a Cadência do Movimento](part-2/06-cadencia.md) 17 | - [Adicionando um Rabo a Cobra](part-2/07-rabo.md) 18 | - [Colisões](part-2/08-colisoes.md) 19 | - [Migrando versões da Bevy](part-2/09-migrando-versoes.md) 20 | - [Multiplayer Local](part-2/10-multiplayer.md) 21 | -------------------------------------------------------------------------------- /src/imagens/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/.DS_Store -------------------------------------------------------------------------------- /src/imagens/animation_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/animation_time.jpg -------------------------------------------------------------------------------- /src/imagens/interpolation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/interpolation.jpg -------------------------------------------------------------------------------- /src/imagens/ping_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/ping_time.jpg -------------------------------------------------------------------------------- /src/imagens/reconciliacao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/reconciliacao.jpg -------------------------------------------------------------------------------- /src/imagens/simpleblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/simpleblock.png -------------------------------------------------------------------------------- /src/imagens/snake_pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/snake_pixel.png -------------------------------------------------------------------------------- /src/imagens/step_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/step_time.jpg -------------------------------------------------------------------------------- /src/imagens/sync_problem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naomijub/Rust-game-dev/f64638da97065f56fb2060f93fe238e2cb674c8e/src/imagens/sync_problem.jpg -------------------------------------------------------------------------------- /src/part-1/00-capa.md: -------------------------------------------------------------------------------- 1 | # Conceitos importantes para o desenvolvimento de serviços de jogos digitais 2 | 3 | 1. O problema e sua arquitetura básica. 4 | 2. Predição e reconciliação. 5 | 3. Interpolação de entidades. 6 | 4. Compensação de lag. -------------------------------------------------------------------------------- /src/part-1/01-arq.md: -------------------------------------------------------------------------------- 1 | # O problema e sua arquitetura básica. 2 | 3 | Neste capítulo vamos entender quais os problemas que serviços para games enfrentam e quais são algumas das formas de resolvê-los para obtermos um conjunto de serviços que tornam o desenvolvimento de jogos multiplayer uma realidade. 4 | 5 | > *Multiplayer* 6 | > 7 | > Jogos multiplayer são jogos com mais de uma pessoa jogando simultaneamente se conectando através de um servidor. 8 | 9 | ## Introdução 10 | 11 | Desenvolver um jogo é bastante complicado, agora desenvolver um jogo para mais de uma pessoa jogando é ainda mais complicado. Felizmente, podemos resumir os problemas que servidores de jogos possuem em duas categorias: 12 | 13 | 1. Humanos maliciosos. 14 | 2. Física realística. 15 | 16 | ### Humanos Maliciosos 17 | 18 | Tudo começa com o desejo das pessoas de trapacear em um jogo. 19 | 20 | Podemos dizer que para jogos single-player, ou de somente uma pessoa jogando, trapacear afeta a experiência, mas é uma escolha da pessoa burlar a experiência do jogo, a final a trapaça não afeta ninguém além da pessoa, porém para jogos multiplayer o cenário é diferente. Em um jogo multiplayer, uma pessoa burlando as regras do jogo pode conseguir algumas vantagens que além de afetar sua experiência, tornam a experiência das outras pessoas muito pior. Alguns exemplos que já vi na minha vida: 21 | 22 | * Vida muito maior que 100%, ou seja, a pessoa possuia 1000% de vida em uma partida, tornando ela quase imortal, já que colecionava muito mais recursos. 23 | * Tiros duplos ou triplos, ou seja, para cada vez que a pessoa realizava um tiro, duas ou três balas eram enviadas ao mesmo tempo, reduzindo muito as chances do alvo de sobreviver. 24 | * Atravessar paredes, não sei bem como este mod funcionava, mas acredito que projetava a pessoa para além do objeto de colisão. 25 | * Paredes invisíveis, ou seja, a pessoa havia removido a renderização de objetos inanimados, o que a permitia visualizar todos os alvos antes de ser percebida. 26 | * Velocidade 2, ou seja, para cada passo da pessoa, o jogo a movia 2 vezes mais rápido. 27 | 28 | Tendo estes eventos em mente, podemos concluir que existe uma única solução realmente confiável para um servidor NÃO CONFIAR NO USUÁRIO. 29 | 30 | ## Como não confiar no usuário? 31 | 32 | A resposta para está pergunta é na verdade bastante simples, o cliente, ou seja a pessoa jogando, deve fornecer o mínimo de informações em relação ao seu posicionamento, balas disparadas, direção, etc. Enquanto isso, o servidor deve ser autoritário, recendendo estes comandos básicos e informado para o cliente o que está acontecendo. Em outras palavras, o cliente envia comandos e botões pressionados para o servidor, o servidor executa o próximo passo do jogo e devolve ao cliente as novas informações. Isso não vai impedir que o servidor seja explorado de vulnerabilidades, mas reduzirá drasticamente a capacidade de uma pessoa jogando de trapacear. Assim, para o caso da pessoa que está dando tiros múltiplos, ela pode até ver 3 tiros saindo de sua arma, mas o servidor reconhecerá somente 1 e propagará ao resto do jogo somente 1. 33 | 34 | Resumindo, o gerenciamento do estado do jogo é realizado apenas pelo servidor. Clientes enviam apenas suas interações com o controle, teclado e mouse para o servidor. O servidor atualiza o estado do jogo e envia esta informação de volta aos clientes que apenas renderizam ela em sua tela. 35 | 36 | ## O problema com a física 37 | 38 | Parece uma solução perfeita né? Infelizmente ela funciona bem somente quando o jogo é baseado em turnos, como jogos de carta e alguns RPGs, ou a rede é em LAN, já que neste cenário a comunicação com o servidor é considerada instantânea. Para jogos como Call of Duty e Rainbow Six está estratégia vai contar com um enorme delay já que precisam se conectar com servidores distantes. 39 | 40 | Assim, vamos supor o meu cenário. Mesmo que minha conexão à internet seja sensacional (mentira, isso não existe), estou em Porto Alegre e o servidor mais próximo está em São Paulo para o jogo X. Porto Alegre e São Paulo estão distantes entre si mais de 1100 km. Na física a velocidade da luz é a maior velocidade atingível por um corpo (photons no caso), ou seja 300.000 km/s no vácuo, assim a luz levaria 3,7 milisegundos para percorrer os 1100 km (1100/300000 = 0,0036667 segundos). Essa é a velocidade da luz no vácuo, parece bem otimista né? Mas neste caso estamos falando de bytes trafegando pela internet, que na prática são elétrons e pulsos de luz trafegando por um cabo, e provavelmente não em linha reta, o que deve aumentar esse valor de 3,7 por alguns microsegundos. Existe mais um fator importante em como a internet funciona, os dados trafegados pela internet são na verdade uma séries de pacotes, ou *hops*, que trafegam de um roteador ao outro, certamente abaixo da velocidade da luz. Além disso, roteadores possuem um atraso extra, já que todos os pacotes devem ser abertos, copiados e inspecionados para então serem reroteados a seus destinos finais. 41 | 42 | Vamos então dizer que o atraso dos meus pacotes até São Paulo leva 25 ms, o que seria um tempo excepcional (neste momento um ping da minha máquina ao google.com está levando entre 25 e 30 ms), mas tempos de 50 ms e até 200 ms não seria impressionantes para certas situações. Agora vamos dizer que nossa jogadora apertou para atirar no momento x, isso quer dizer que nosso servidor receberá a ação de atirar 25 ms depois. Digamos que nosso servidor processe o evento em um tempo substancialmente menor que 1 milisegundo, algo como 500 us, isso quer dizer que quando o servidor responder, a jogadora receberá essa atualização 50 ms depois de ter clicado para atirar. Humanos em média enxergam 25 frames por segundo, o que indica que o delay já é maior que nossa capacidade de observação por 10 ms. Esses 10 ms de delay na nossa percepção já são suficiente para termos uma experiência ruim de jogabilidade, ou seja, o famoso *lag*, ou atraso. A imagem a seguir demonstra este efeito: 43 | 44 |  45 | -------------------------------------------------------------------------------- /src/part-1/02-prd-rec.md: -------------------------------------------------------------------------------- 1 | # Predição e Reconciliação 2 | 3 | No capítulo anterior falamos sobre o lag, ou atraso entre ação no cliente e a atualização enviada pelo servidor nos baseando no modelo de cliente servidor na qual o cliente não responde seu estado, mas sim a ação desejada, para que o servidor atualize seu estado. Um jogo que pode levar algumas frações de segundo para atualizar o estado pode ser considerado de jogabilidade ruim ou injogável devido ao lag de renderização. Assim, neste capítulo vamos explorar uma solução para minimizar este problema. 4 | 5 | ## Predição pelo lado do cliente 6 | 7 | Como a maior parte dos jogos é deterministico, ou seja, não há aleatoriedade no resultado, podemos prever qual vai ser o próximo passo do jogo antes do servidor responder. Para maior parte das pessoas jogando esta experiência será "idêntica" ao jogo sem servidor, mas para as pessoas trapaceando a experiência não será realistica, desfavorecendo o jogo com trapaças. Assim, podemos assumir que nosso servidor receberá ações válidas para 99% dos casos, nos permitindo prever o próximo instante. 8 | 9 | No cenário que descrevemos anteriormente nossa ação com o servidor levava 50 ms para atualizar o estado do jogo, para só então uma animação ser ativada (digamos que ela leve mais 50 ms) como a imagem a seguir nos mostra: 10 | 11 |  12 | 13 | Nessa imagem podemos ver que o atraso do servidor (50 ms) mais o tempo de animação (50 ms) fará com que percebemos o tiro apenas 100 ms depois dele ter sido realizado, ou seja, no terceiro frame que nosso olho detecta, certamente uma experiência desagradável. 14 | 15 | Como o jogo nosso jogo é deterministico, podemos presumir que a ação será executada com sucesso no servidor, aplicar nossas regras locais de validação e iniciar a animação do tiro no momento em que pressionamos o botão para realizar a ação. Para a grande maioria dos casos a atualização do servidor e o final da animação vão coincidir em estado e fizemos um predição bem sucedida, fazendo com que não exista atrasos entre a ação e a renderização. Para os casos de trapaça a animação ocorrerá, mas em nada afetará o estado geral do jogo, somente afetará negativamente a experiência do usuário trapacendo. 16 | 17 | ### Problemas de sincronização 18 | 19 | Infelizmente essa estratégia não é perfeita e problemas de sincronização ou eventos conflitantes podem acontecer. Imagine agora o cenário na qual o personagem está se movimentando e o tempo de atraso é 75 ms em vez dos 50 ms anteriores, o tempo da animação é de 30 ms e a pessoa pressiona para se movimentar para frente 2 vezes seguidas. A imagem a seguir e os passos marcados na imagem exemplificam: 20 | 21 |  22 | 23 | 0. Personagem está o ponto `(0,0)` no instante 0 ms. 24 | 1. Neste mesmo instante a pessoa pressiona para se movimentar enviando uma ação para o servidor que durará 75 ms. 25 | 2. A ação do passo 1 ativou uma animação que moveu o personagem para a posição `(0,1)` 30 ms depois. 26 | 3. Na posição `(0,1)` uma nova ação de movimentação acontece, enviando esta nova ação para o servidor que durará mais 75 ms. 27 | 4. A ação do passo 3 ativou uma nova animação que moveu o personagem para a posição `(0,2)` 30 ms depois. Já se passaram 60 ms. 28 | 5. 15 ms depois de terminar a ação 4, o servidor respondeu a ação 1 fazendo o personagem voltar para posição `(0,1)`. Já se passaram 75 ms. 29 | 6. 30 ms depois de terminar a ação 5, o servidor respondeu a æção 3 fazendo o personagem voltar para posição `(0,2)`. Ja se passaram 105 ms. 30 | 31 | Com este detalhamento podemos ver que pelo ponto de vista da pessoa jogando, o personagem vai responder as duas primeiras ações se movimentando até a posição `(0,2)` para então voltar para posição `(0,1)` e depois ainda voltar para posição `(0,2)` gerando uma péssima experiência de jogo, forçando assim a adotarmos uma estratégia de reconciliação. 32 | 33 | ## Reconciliacão pelo servidor 34 | 35 | A chave deste problema é entender a diferença temporal dos cliente e do servidor, já que o cliente vê o jogo em tempo real (presente) e o servidor autoritário está no passado. Assim, sempre haverá uma diferença de sequência de comandos a serem processados entre o cliente e o servidor. Felizmente isso não é muito difícil de resolver. 36 | 37 | Primeiro passo é fazer com que o cliente salve suas ações em uma sequência de comandos, assim a primeira movimentação seria a ação `#1` e a segunda movimentação seria a ação `#2`. Logo, o servidor poderá respoderá responder uma ação identificando a qual comando ela pertence. A figura a seguir exemplifica o que acontece: 38 | 39 |  40 | 41 | 1. O evento `#1` é lancado, 30 ms depois da animação a posição `#1 => (0,1)` é registrada e 38 ms depois o servidor recebe a ação `#1`. A sequência de comandos é `[#1 => (0,1)]`. 42 | 2. O evento `#2` é lancado, 30 ms depois da animação a posição `#2 => (0,2)` é registrada e 38 ms depois o servidor recebe a ação `#2`. A sequência de comandos é `[#1 => (0,1), #2 => (0,2)]`. 43 | 3. O evento `#1` é retornado pelo servidor com o valor `#1 => (0,1)`. A função `check` para o estado da sequência de comandos atual (`[#1 => (0,1), #2 => (0,2)]`) e o evento `#1 => (0,1)` recebido é executado para reconciliar. Remove todos os comandos até `#1 => (0,1)` da sequência de comandos. 44 | 4. O evento `#2` é retornado pelo servidor com o valor `#2 => (0,2)`. A função `check` para o estado da sequência de comandos atual (`[#2 => (0,2)]`) e o evento `#2 => (0,2)` recebido é executado para reconciliar. Remove todos os comandos até `#2 => (0,2)` da sequência de comandos. 45 | 5. Sequência de comandos é `[]`. 46 | 47 | > **Descrição da função `check`** 48 | > 1. Argumentos são **sequência de comandos executados** e **evento #**. 49 | > 2. Verifica se o valor de `#n` na sequência de comando é igual ao que o servidor retornou. Caso não for igual retorna erro. 50 | > 3. Aplica o próximo evento, `#n+1`, ao resultado do evento `#n`. Caso o resultado de `#n` mais o evento `#n+1` não corresponder ao evento salvo na sequência de comandos para `#n+1` retornar erro. 51 | > **Observação**: Se o evento que o servidor responder não for `#n` esperado, podemos concluir que o pacote se perdeu ou o servidor retornou um erro, assim existem duas alternativas **1.** descartar todos os pacotes até o evento recebido e fazer o check, ou **2.** aplciar todos os eventos anteriores até o evento recebido. Particularmente vejo a soluação **1** sendo a mais comum, pois sabemos que o estado anterior está certo. 52 | 53 | Este é um exemplo bem simples de movimentação e bastante intuitivo de visualizar, mas as aplicações de predição e reconciliação podem ser feitas em praticamente qualquer área do jogo e qualquer tipo de jogo. Imagine um jogo de corrida multiplayer e você está na linha de chegada em velocidade máxima, com um carro logo atrás de você. No próximo segundo considerando as atuais circunstâncias, é óbvio que você vai ganhar, pois você está na frente do outro carro e com uma velocidade maior, mas agora imagine que alguns milésimos antes do final da corrida a outra pessoa apertou o botão de nitro e te ultrapassou. A predição diria que seu carro ganharia a corrida, mas o servidor disse que não e você ficou em segundo lugar. Isso nos leva a um ponto interessante, mesmo em ambientes determinísticos, existe a chance da predição e da reconciliação não serem iguais, Para um cenário de fim de jogo como descrito aqui é bastante trivial a resposta, ignore a predição e responda com o resultado do servidor, porém se isso acontecer frequentemente no meio do jogo a experiência de jogabilidade vai ser ruim. 54 | 55 | No próximo capítulo vamos explorar como resolver este problema de predição e reconciliação através de interpolação de entidades. 56 | -------------------------------------------------------------------------------- /src/part-1/03-interpolacao.md: -------------------------------------------------------------------------------- 1 | # Interpolação de Entidades 2 | 3 | Nos capítulos anteriores lidamos com o problema de uma pessoa poder trapacear e como fazer com que o jogo se mantenha conciliado com um servidor autoritário dando a sensação de que o servidor não existe, porém não expandimos este problema para quando estamos lidando com mais de uma pessoa jogando online. Neste capítulo vamos explorar técnicas que nos permitem manter a jogabilidade quando várias pessoas estão interagindo umas com as outras em um ambiente online. 4 | 5 | ## Lidando com centenas de ações simultâneas 6 | 7 | No capítulo anterior falamos sobre o servidor processar uma sequência de comandos e retornar como eventos autoritários para o cliente. Imagine agora que este cliente está alucinadamente mandando eventos para o servidor e que ele não está sozinho, pois existem mais uma dezena de clientes mandando eventos simultaneamente para o servidor. Sendo assim, atualizar o estado do jogo para cada comando recebido de cada cliente e depois transmitir o estado do jogo de volta para cada cliente consumiria muita CPU e muita banda. 8 | 9 | Tendo em vista evitar o consumo desnecessário de CPU e banda outra abordagem parece fundamental. Esta nova abordagem consiste em enfileirar os comandos que os clientes enviam, sem processar eles, e em vez de atualizar o estado do jogo imediatamente para cada comando, fazemos atualizações periódicas e de baixa frequência, por exemplo 10 vezes por segundo. Este atraso entre cada update, no caso do nosso exemplo de 100 ms, é chamado de *time step*, ou passo temporal. O *time step* é definido como uma iteração de loop de update na qual todas as informações não processdas de todos clientes são aplicadas e o novo estado é transmitido para todos os clientes. Ou seja, o estado do jogo é atualizado com uma periodicidade específica de forma independente e não é afetado pela quantidade de clientes e seus comandos. 10 | 11 | **Obs**: Muitas vezes a física do jogo é atualizada em passos de tempo menor para aumentar a previsibilidade. 12 | 13 | ## Updates de baixa frequência 14 | 15 | Seguindo com o conceito de um update de estado a cada 100 ms um novo problema aparece, os outros clientes não tem ideia de como seus oponentes estão se atualizando, gerando eventos que parecem bastante bruscos a cada atualização. Ou seja, predição e reconciliação funcionam muito bem para o lado do cliente, mas não para o resto das pessoas jogando. A imagem a seguir detalha melhor essa situação: 16 | 17 |  18 | 19 | Na imagem anterior podemos ver o mesmo cenário de predição e reconciliação funcionando muito bem para o `Cliente 1`, permitindo que sua jogabilidade seja coerente com a jogabilidade de um jogo single-player, porém para o `Cliente 2` podemos ver que as transições `(0,0) -> (0, 1)` e `(0, 1) -> (0, 2)` do `Cliente 1` são bruscas para o `Cliente 2`, já que estas atualizações dependem exclusivamente das atualizações do servidor. 20 | 21 | Agora voltando ao exemplo dos carros que mencionamos no final do capítulo anterior. Estamos em uma situação na qual temos controle do nosso carro, mas o carro da outra pessoa é determinado pelo servidor. Se este carro recebe atualizações apenas a cada 100 ms, teremos uma animação péssima de seu deslocamento, nos obrigando a encontrar outra solução para melhorar a experienência. Esta outra soluação envolve fazer a predição da posição do outro carro do lado do nosso cliente, pois sabemos sua direção, sua velocidade e temos certeza que o carro não fará um movimento radical, como girar 180 graus. Sendo assim, se o outro carro está indo reto com uma velocidade de 100 km/h, podemos prever que nos próximos 100 ms o carro estará 0,2 metros a frente de onde ele está neste exato segundo. Essa predição pode parecer maravilhosa, já que ele só se deslocou 0,2 metros em linha reta, mas infelizmente 100 ms é tempo suficiente para muitas outras coisas acontecerem como uma curva aparecer, bater em um poste, desacelerar ou até mesmo frear bruscamente. Chamamos está técnica de **dead reckoning**. Portanto, o **dead reckoning** é uma técnica de predição dos movimentos de outras pessoas em jogos na qual sua posição, velocidade e direção não são afetadas de forma instantânea, permitindo uma pequena margem para prever movimentos sem grandes danos à experiência. Caso alguma ação inesperada aconteça aceitamos que vamos conviver com uma cena estranha. 22 | 23 | > **Dead reckoning** é originalmente uma estratégia militar para prever a próxima localização de um navio, que se move lentamente e sem grandes oscilações de direção, para que se possa prever onde um torpedo precisa ser lançado para acertar o navio. 24 | 25 | ## E para cenários muito dinâmicos? 26 | 27 | Como falamos anteriormente, *dead reckoning* é bom para jogos que não são tão dinâmicos, como jogos de corrida, porém para jogos na qual as pessoas jogando se movimentam constantemente, atiram, se abaixam, pulam, giram 180 graus é impossível prever o próximo passo da pessoa apenas com dados anteriores. Se aplicássemos dead reckoning em um jogo de tiro veríamos personagens se teletransportando pequenas distâncias, múltiplas balas saindo de diferentes lugares e personagens fazendo movimentos impossíveis. Sendo assim, outra estratégia é necessária para jogos de tiro, sendo essa a **interpolação de entidades**. 28 | 29 | No cenário descrito do parágrafo anterior, temos certeza apenas de 1 coisa, que a cada 100 ms temos uma atualização das informações do estado do jogo e dos personagens. Tendo em vista que sabemos o passado todo, o truque é mostrar para pessoa jogando o que acontece entre esses dados que já sabemos. Ou seja, a solução é mostrar para a pessoa que está jogando o passado relativo dos outros personagens. Isso que chamamos de **interpolação de entidades**. 30 | 31 | Explicando melhor, podemos dizer que no momento `t = n + 1`, que você acabou de receber, a posição do momento `t = n` é conhecida. Sendo assim, neste momento `t = n + 1` conhecemos as posições referentes a `t = n` e `t = n + 1`. Portanto, para o momento `t = n + 2` mostramos o passado, ou seja, o que ocorreu no momento `t = n = 1` e para o momento `t = n + 1` mostramos o que ocorreu no momento `t = n` do outro personagem. Deste modo o servidor está sempre mostrando as informações reais de movimentação dos outros personagens, porém com um "pequeno atraso" de 100 ms. A imagem a seguir exemplifica: 32 | 33 |  34 | 35 | O diagrama de interpolação nos mostra bem como estamos prevendo os passos intermediários. Para um momento inicial estamos com a posição `P(0,1)`, depois o servidor nos atualiza com a posição `P(0,1)` novamente, neste momento exibimos a posição que conheciamos antes do *step time*, a `V(0,1)`. Quando recebemos a posição `P(0,2)`, mantemos a posição `V(0,1)`, que havia sido entregue anteriormente pelo servidor. Agora sabemos o vetor de posições `[P(0, 1), #1 P(0, 1), #2 P(0, 2)]`, e podemos interpolar que no próximo *step time* nosso personagem inimigo vai para a posição `V(0,2)` passando pela posição `V(0,1.75)`, melhorando a experiência da pessoa jogadora. 36 | 37 | Na maior parte dos casos interpolação funciona muito bem, porém existem alguns casos que pode ser importante enviar mais informações de posições intermediárias entre `#1` e `#2`. Ou seja, se atualizações de estado a cada 100 ms não são suficientes, podemos enviar as últimas 10 atualizações que ocorreram com intervalos de 10 ms, que certamente vai fazer com que seu jogo pareça mais realista. Note que está técnica faz com que cada jogadora perceba pequenas variações do ambiente do jogo em relação às outras pessoas, que geralmente não é algo perceptível. Infelizmente, nada é perfeito e existem exceções como no caso de quando damos um tiro, pois estamos atirando na personagem da outra pessoa de 100 ms atrás. É nesse caso que precisamos explorar o último tópico desta parte, compensação de lag. 38 | -------------------------------------------------------------------------------- /src/part-1/04-lag.md: -------------------------------------------------------------------------------- 1 | # Compensacão de Lag 2 | 3 | O cenário que temos até agora parece funcionar muito bem para percebermos movimentações, pois temos: 4 | 5 | * Dado um tempo n, nosso servidor recebe informações de todos os clientes. 6 | * Servidor processa todas as informações e transmite as atualizações. 7 | * Estas atualizações são periódicas e de baixa frequência. 8 | * Clientes enviam informações e verificam seus efeitos localmente. 9 | * Clientes recebem as atualizações de estado do jogo: 10 | 1. Reconciliam com os efeitos que previram. 11 | 2. Interpolam os efeitos dos outros personagens. 12 | * Cliente se vê no presente, mas vê os outros cliente no passado. 13 | 14 | Esta situação é geralmente ótima, a menos quando precisamos garantir situações como um tiro na cabeça, que qualquer pequena variação pode causar um erro, pois as informações de tempo e espaço são muito sensíveis. É ai que entra a compensação de lag. 15 | 16 | Imagine o cenário na qual você é uma sniper mirando perfeitamente na cabeça de um personagem "imóvel", um tiro dificil de errar. Você atira e, magicamente, nada acontece. Você se irrita, sai da partida e desliga o jogo pensando como pode ter errado aquele tiro perfeito e, pior, a pessoa que você devia ter matado te matou. Este é o efeito de lag temporal, pois seu tiro ocorreu em um personagem que estava 100 ms no passado, para quem gosta de física, é como se a velocidade da luz fosse muito muito muito inferior a que realmente é. Felizmente, existem algumas estratégias para resolver este efeito. Vamos detalhar como isso pode ser reolvido: 17 | 18 | 1. Você deu um tiro, seu cliente enviou as informações para o servidor, mas desta vez enviou mais informações além do botão que você clicou, pois enviou o botão que você apertou, o exato momento temporal que você apertou o botão (e se o botão de mira estava sendo apertado) e o que estava exatamente em sua mira neste instante. 19 | 2. Como o servidor está recebendo todos momentos temporais, ele pode reconstruir os eventos temporalmente ordenados, ou seja, o servidor pode reconstruir o mundo no exato momento de seu tiro, assim como para todos outros clientes. 20 | 3. Sabendo o que sua arma estava mirando no momento de seu tiro, a cabeça de seu inimigo, seu presente passa a ser considerado como válido no servidor, já que ele compensa esta diferenca. 21 | 4. O servidor processa o tiro e transmite para todos os clientes, deixando seu oponente furioso por ter levado um headshot. 22 | 23 | E é no passo dois que a compensacão de lag ocorre. 24 | 25 | ## Conclusão 26 | 27 | Primeira coisa que fizemos foi entender qual o grande problema do desenvolvimento de servidores para jogos, pessoas querendo trapacear, e a partir disso entendemos qual a solução básica, um cliente que só envia comandos pro servidor e um servidor autoritário. Vimos que com um servidor autoritário alguns problemas de defasamento temporal pode ocorrer entre a informação que temos e a informação que o servidor nos obriga a ter. Para reduzir estes problemas aprendemos as técncias de predição e de reconciliação, mas descobrimos problemas de sincronização com outros clientes. Para resolver os problemas de sincronização aprendemos as técncias de dead reckoning e interpolação de entidades, que são ótimas técnicas, mas ainda podem falhar na hora que ações muito sensíveis espacialmente são executadas. Para resolver este problema de ações sensíveis, aprendemos compensação de lag, mas ainda nos falta por a mão na massa. Nos próximos capítulos vamos explorar um jogo simples de tiro e um exemplo de servidor para ele. 28 | -------------------------------------------------------------------------------- /src/part-2/00-intro.md: -------------------------------------------------------------------------------- 1 | # Multiplayer Snake Game 2 | 3 | Vulgo jogo da cobrinha online e em Rust. 4 | 5 | ## Sobre esta seção do livro 6 | 7 | Este era um projeto que surgiu inicialmente como um livro para a Casa do Código (Visivelmente minha editora favorita), mas infelizmente a engine que eu estava usando foi "descontinuada" e decidi que queria um livro mais vivo, que pudesse se adaptar mais rapidamente a evolução das engines, assim como novas versões do Rust e o mundo de desenvolvimento de jogos. Assim, para este livro comecei a pesquisar qual seria o jogo mais didático e menos cansativo para desenvolver na [Bevy Engine](https://bevyengine.org/), a nova engine promissora de Rust, optando por traduzir este [tutorial](https://mbuffett.com/posts/bevy-snake-tutorial/) e adicionar o fator multiplayer local nele. 8 | 9 | > A engine descontinuada que menciono é a https://amethyst.rs/. 10 | 11 | ## Organização 12 | 13 | - Sobre a Bevy Engine e configurando uma janela vazia. 14 | - ECS 15 | - Snake Game 16 | - Multiplayer local de Snake Game -------------------------------------------------------------------------------- /src/part-2/01-bevy.md: -------------------------------------------------------------------------------- 1 | # Sobre a Bevy 2 | 3 | Bevy engine é uma das game engines mais promissoras do mercado e um grande esforço coletivo para a comunidade rust_gamedev. Se trata de uma engine orientada a dados, gratuíta e open source, sob as licenças Apache e MIT, ou seja, perfeita para qualquer projeto. Ela possui como objetivos de design: 4 | 5 | * Um conjunto completo de features para jogos 2D e 3D, podendo inclusive ser aplicada para outros objetivos. 6 | * Simples e poderosa, mas mantendo o fácil aprendizado. 7 | * Orientada a dados utilizando o paradigma ECS (Entity component system, no próximo capítulo). 8 | * Modular, use o que quiser, adicione o que quiser, e substitua o que quiser. 9 | * Rápida, paralela e em Rust <3. 10 | * Compilação rápida 11 | 12 | A atual versão da Bevy é [](https://crates.io/crates/bevy) e este livro atualmente foi atualizado para a versão `0.12`. A compatibilidade com Rust esta garantida para a versão `1.73`. 13 | 14 | ## Iniciando o projeto 15 | 16 | > Para iniciar um projeto com a Bevy é necessário possuir Rust e Cargo, caso você não possua basta fazer download em https://rustup.rs/. 17 | 18 | Vamos iniciar nosso projeto com um simples `cargo new bevy-snake --bin`, que gera um projeto executável em Rust chamado `bevy-snake`. Este projeto vai possuir um `Cargo.toml` (onde os metadados do projeto estão localizados), um `src/main.rs` e um `.gitignore`: 19 | 20 | ```rust 21 | // src/main.rs 22 | fn main() { 23 | println!("Hello, world!"); 24 | } 25 | ``` 26 | 27 | ```sh 28 | # .gitignore 29 | /target 30 | ``` 31 | 32 | Agora adicionamos versão atual da bevy (`bevy = "0.12"`) a seção `[dependencies]` do Cargo.toml. Adicionamos também a crate de aleatoriedade `rand`: 33 | 34 | ```toml 35 | [dependencies] 36 | bevy = "0.12" 37 | rand = "0.8.5" 38 | ``` 39 | 40 | ou simplesmente 41 | 42 | ```bash 43 | cargo add bevy@0.12 44 | cargo add rand@0.8.5 45 | 46 | ``` 47 | 48 | Com essas mudanças no `Cargo.toml` podemos começar a usar o `prelude` da bevy e criar nosso primeiro app com: 49 | 50 | ```rust 51 | use bevy::prelude::*; 52 | 53 | fn main() { 54 | App::new().run(); 55 | } 56 | ``` 57 | 58 | ### Instanciando uma Janela 59 | 60 | Instanciar uma janela com a Bevy é bastante trivial e pode ser feito através do uso de plugins, neste caso o `DefaultPlugins` contém um conjunto básico de plugins que tornam a bevy operacional: 61 | 62 | ```rust 63 | fn main() { 64 | App::new().add_plugins(DefaultPlugins).run(); 65 | } 66 | ``` 67 | 68 | Agora se executarmos `cargo run` veremos uma janela com fundo cinza. Por padrão, os plugins da Bevy não incluem camera, pois o uso de camera é muito variado em jogos, assim, precisamos criar nosso próprio sistema de cameras. Usaremos uma camera 2D com o commando `CameraBundle2D::default()` em uma função que fará a configuração do sistema de cameras inicial alterando a variável do tipo `mut Commands`. `Commands` é um tipo muito comum ao escrever sistemas com a Bevy e é usado para enfileirar comandos com o objetivo de modificar o mundo (que chamaremos de `world`) e os recursos (que chamaremos de `resources`). Assim, na função a seguir, `setup_camera`, receberemos como argumento `mut commands: Commands` e utilizaremos ele para instanciar (chamado de `spawn`) uma nova entidade bundle com os componentes de uma câmera 2D: 69 | 70 | ```rust 71 | fn setup_camera(mut commands: Commands) { 72 | commands.spawn(Camera2dBundle::default()); 73 | 74 | } 75 | ``` 76 | 77 | E agora basta adicionar esse função ao nosso `App` através de um `add_systems` que é o método responsável por adicionar esse tipo de recurso no nosso `App`: 78 | 79 | ```rust fn main() { 80 | App::new() 81 | .add_plugins(DefaultPlugins) 82 | .add_systems(Startup, setup_camera) 83 | .run(); 84 | } 85 | 86 | fn setup_camera(mut commands: Commands) { 87 | commands.spawn(Camera2dBundle::default()); 88 | } 89 | ``` 90 | 91 | ## Plugins 92 | 93 | A Bevy é pensada de forma que todas suas partes sejam modularizáveis, assim, todas as core features da engine são implementadas como plugins que podem ser substituídos, evoluídos e customizados, além disso, os próprios jogos são encarados como plugins. Assim, se você não precisar de uma UI, basta não registrar o sistema de UI, quer um sistema de UI diferente, registre o seu próprio. Para o caso de servidores, basta não registrar o plugin `RenderPlugin`. 94 | 95 | Caso você não precise de uma experiência tão avançada com a Bevy, é possível utilizar o `DefaultPlugins` que utilizamos anteriormente, que possui sistemas como Rendering, gerenciamento de assets, sistema de UI, janelas e gerenciamento de entrada de dados. 96 | 97 | ### Criando um Plugin 98 | 99 | Para criar um plugin simplesmente precisamos implementar a trait `Plugin` em um tipo que comporte as informações necessárias. No caso do plugin que vamos implementar é apenas um `hello world` para plugins, então não precisamos de dados, criando apenas um 100 | 101 | ```rust 102 | pub struct HelloPlugin; 103 | 104 | impl Plugin for HelloPlugin { 105 | fn build(&self, app: &mut App) { 106 | // lógica do plugin 107 | } 108 | } 109 | ``` 110 | 111 | Agora precisamos de uma função que nosso sistema vai executar, neste caso um simples `println`: 112 | 113 | ```rust 114 | fn hello_plugin() { 115 | println!("hello plugin!"); 116 | } 117 | 118 | ``` 119 | 120 | E adicionamos essa função como um `startup_system` no nosso plugin: 121 | 122 | ```rust 123 | impl Plugin for HelloPlugin { 124 | fn build(&self, app: &mut App) { 125 | app.add_systems(Startup, hello_plugin); 126 | } 127 | } 128 | 129 | ``` 130 | 131 | Por último, basta adicionarmos nosso plugin ao `App` principal e executar `cargo run`: 132 | 133 | ```rust 134 | fn main() { 135 | App::new() 136 | .add_startup_system(setup_camera) 137 | .add_plugins(HelloPlugin) 138 | .add_plugins(DefaultPlugins) 139 | .run(); 140 | } 141 | ``` 142 | 143 | Veremos algo no terminal como: 144 | 145 | ``` 146 | 2022-06-20T05:28:52.725036Z INFO bevy_render::renderer: AdapterInfo { name: "AMD Radeon Pro 5500M", vendor: 0, device: 0, device_type: DiscreteGpu, backend: Metal } 147 | hello plugin! 148 | ``` 149 | -------------------------------------------------------------------------------- /src/part-2/02-ecs.md: -------------------------------------------------------------------------------- 1 | # Entity Component System (ECS) 2 | 3 | O sistema de gerenciamento de dados da Bevy é chamado de *Entity Component System*, ou **ECS**, e sua principal característica é a simplicidade do gerenciamento de dados. Uma boa analogia ao seu funcionamento é com bancos de dados tabulares, na qual os componentes, *components*, são os tipos de dados, ou colunas, e as entidades, *entities*, são as linhas, mais especificamente o ID das linhas. Por exemplo, você poderia ter diversas entidades com o componente `Health` e cada entidade possui um component `Health` diferente, já que NPCs, players e objetos do mundo podem ter `Health`s diferentes (*health* significa vida em inglês). Assim, o conjunto de componentes que uma entidade possui é chamado de arquétipo, *Archetype*. 4 | 5 | Considerando a entidade player possuindo componentes como vida, força, ataque, defesa, inventario, as entidades inimigos com vida, força, ataque, defesa, inteligência, e a entidade planta com apenas vida, fica muito fácil escrever uma lógica de jogo que gerencia esses tipos de entidades, como verificar se uma entidade com vida encontrou outra entidade com vida, simplificando muito a criação de lógicas de jogo. Essa lógica de gerenciamento é chamado sistema, *system*. Estes sistemas são executados em paralelo pelo *smart scheduling algorithm* da Bevy e com isso devemos manter nossas entidades o mais horizontal possível, evitando grandes componentes com muitos campos. Isso influência muito a performance do sistema, pois quando mais vertical a entidade mais problemas de acesso aos dados em paralelo teremos. 6 | 7 | > Para você que vem da orientação a objectos, no paradigma de ECS é mais comum possuir uma entidade com diversos componentes, como a entidade Player que possui os componentes Vida(u32), Posição(x, y, z), Direção(x, y, z), Escala(x, y, z), Rotação(x, y, z), Defesa(u16), Ataque(u16), Força(u16) em vez de uma classe `Player` com os campos vida: u32, posição: [x, y, z], direção: [x, y, z], escala: [x, y, z], rotação: [x, y, z], defesa: u16, ataque: u16, força: u16: 8 | 9 | ```rust 10 | // Prefira isso: 11 | // Entidade Player; 12 | 13 | #[derive(Component)] 14 | pub struct Vida(u32) 15 | 16 | #[derive(Component)] 17 | pub struct Posição(x, y, z) 18 | 19 | #[derive(Component)] 20 | pub struct Direção(x, y, z) 21 | 22 | #[derive(Component)] 23 | pub struct Escala(x, y, z) 24 | 25 | #[derive(Component)] 26 | pub struct Rotação(x, y, z) 27 | 28 | #[derive(Component)] 29 | pub struct Defesa(u16) 30 | 31 | #[derive(Component)] 32 | pub struct Ataque(u16) 33 | 34 | #[derive(Component)] 35 | pub struct Força(u16) 36 | 37 | // Em vez disso: 38 | pub struct Player { 39 | pub vida: u32, 40 | pub posição: [x, y, z], 41 | pub direção: [x, y, z], 42 | pub escala: [x, y, z], 43 | pub rotação: [x, y, z], 44 | pub defesa: u16, 45 | pub ataque: u16, 46 | pub força: u16, 47 | } 48 | 49 | ``` 50 | 51 | ## Criando entidades 52 | 53 | Entidades são simplesmente IDs inteiros associados a um comando `spawn` de `commands`, `commands.spawn(...)` e para adicionar componentes basta utilizarmos a diretica `insert` em um `spawn`: 54 | 55 | ```rust 56 | fn spawn_entity(mut commands: Commands) { 57 | commands 58 | .spawn() 59 | .insert(Label("Player")) 60 | .insert(Vida(10)) 61 | .insert(Posição(0, 2, 0)) 62 | .insert(Direção(0, 2, 0)) 63 | .insert(Escala(0, 2, 0)) 64 | .insert(Rotação(0, 2, 0)) 65 | .insert(Defesa(10)) 66 | .insert(Ataque(10)) 67 | .insert(Força(10)); 68 | } 69 | ``` 70 | 71 | Além disso, existe o conceito de *bundles*. *Bundles* são como *templates* que tornam a criação de entidades com diversos componentes mais simples: 72 | 73 | ```rust 74 | #[derive(Bundle)] 75 | struct Transform { 76 | posição: Posição(x, y, z), 77 | direção: Direção(x, y, z), 78 | escala: Escala(x, y, z), 79 | rotação: Rotação(x, y, z), 80 | } 81 | 82 | #[derive(Bundle)] 83 | struct Player { 84 | vida: u32, 85 | defesa: u16, 86 | ataque: u16, 87 | força: u16, 88 | 89 | #[bundle] // Nested bundles 90 | transform: Transform 91 | } 92 | ``` 93 | 94 | Como podemos ver em `transform: Transform`, bundles também podem ser encadeados. Tuplas arbitrárias também são consideradas bundles. Note, que bundles não podem ser consultados com uma *query*. 95 | 96 | ## Recursos (*Resources*) 97 | 98 | Recursos são um tipo de instância que permite armazenar um tipo de dado de forma global, independente de entidades, e qualquer tipo Rust pode ser usado como um recurso independente de implementação de traits. 99 | Para criar um novo recurso você pode simplesmente usar uma `Struct` ou um `enum` e derivar a trait `Resource`. 100 | ```rust 101 | #[derive(Resource)] 102 | struct GoalsReached { 103 | main_goal: bool, 104 | bonus: u32, 105 | } 106 | ``` 107 | Bevy usa resources para muitas coisas. Você pode usa-la para acessar várias features da engine. 108 | 109 | ### Acessando 110 | 111 | Para acessar o valor de um recurso de um systema use `Res/ResMut`: 112 | ```rust 113 | fn my_system( 114 | // irá lançar um panic se o resources não existir 115 | mut goals: ResMut, 116 | other: Res, 117 | // use Option se o resource caso ele possa não existir 118 | mut fancy: Option>, 119 | ) { 120 | if let Some(fancy) = &mut fancy { 121 | // código 122 | } 123 | } 124 | ``` 125 | 126 | ### Gerenciando 127 | 128 | Se você precisa criar/remover `resources` em tempo de execução, você pode usar um `commands` (Commands): 129 | 130 | ```rust 131 | fn my_setup(mut commands: Commands, /* ... */) { 132 | // adicionar (ou sobreescrever) resource, usando os parametros enviados 133 | commands.insert_resource(GoalsReached { main_goal: false, bonus: 100 }); 134 | // garente que o resource exista (criando se necessário) 135 | commands.init_resource::(); 136 | // remove um resource (se ele existir) 137 | commands.remove_resource::(); 138 | } 139 | ``` 140 | 141 | Alternatively, using direct World access from an exclusive system: 142 | Alternativamente, usando diretamente `World` de um `system` exclusivo 143 | 144 | ```rust 145 | fn my_setup2(world: &mut World) { 146 | // Os mesmos metodos e comandos estão disponíveis aqui, 147 | // mas nós podemos também fazer coisas mais sofisticadas 148 | 149 | // verifica se o recurso existe 150 | if !world.contains_resource::() { 151 | // Recebe acesso para o recuros, inserindo um valor customizado caso não tenha passado nenhum 152 | let _bonus = world.get_resource_or_insert_with( 153 | || GoalsReached { main_goal: false, bonus: 100 } 154 | ).bonus; 155 | } 156 | } 157 | ``` 158 | 159 | `Resources` pode também ser configurado no `app buildeer`. Faça isso para recursos que precisam sempre existam quando inicia o app. 160 | 161 | ```rust 162 | App::new() 163 | .add_plugins(DefaultPlugins) 164 | .insert_resource(StartingLevel(3)) 165 | .init_resource::() 166 | // código 167 | ``` 168 | 169 | ### Inicialização 170 | 171 | Existem duas formas de inicializar recursos, a primeira é definindo a trait `Default` para eles, quando eles possuem um tipo de dado simples, já a segunda é implementando a trait `FromWorld` que permite atuar sobre o recurso utilizando valores de `World`: 172 | 173 | Uma inicialização usando `Default` 174 | ```rust 175 | // configurando todos os campos com seu valor default 176 | #[derive(Resource, Default)] 177 | struct GameProgress { 178 | game_completed: bool, 179 | secrets_unlocked: u32, 180 | } 181 | 182 | #[derive(Resource)] 183 | struct StartingLevel(usize); 184 | 185 | // implementação para valores customizados 186 | impl Default for StartingLevel { 187 | fn default() -> Self { 188 | StartingLevel(1) 189 | } 190 | } 191 | 192 | // para enums, você pode espeficar o valor default dele 193 | #[derive(Resource, Default)] 194 | enum GameMode { 195 | Tutorial, 196 | #[default] 197 | Singleplayer, 198 | Multiplayer, 199 | } 200 | ``` 201 | 202 | Para `resources` que precisam de uma inicialização um pouco mais complexa, podemos implementar o `FromWorld` 203 | ```rust 204 | #[derive(Resource)] 205 | struct MyFancyResource { /* stuff */ } 206 | 207 | impl FromWorld for MyFancyResource { 208 | fn from_world(world: &mut World) -> Self { 209 | // Você tem controle total de tudo do seu ECS World aqui 210 | // Por exemplo, você pode aceesar (e modificar!) outros resources 211 | let mut x = world.resource_mut::(); 212 | x.do_mut_stuff(); 213 | 214 | MyFancyResource { /* stuff */ } 215 | } 216 | } 217 | ``` 218 | 219 | A decisão de quando usar recursos ou entity/component é baseada na forma e no momento em que este dado vai ser acessado, mas considerando algo como um jogo com uma unica entidade, pode ainda ser útil utilizar o padrão ECS, pois ele permite maior flexibildiade e compartilhamento de dados, que podem ser muito úteis para a evolução do jogo. 220 | 221 | ## Sistemas (*Systems*) 222 | 223 | Sistemas são funções que a desenvolvedora escreve com o objetivo de ser uma unidade de lógica do jogo atuando sobre as entidades e os componentes. Os sistemas são executados e gerenciados pelas Bevy, mas somente podem ser usados com parâmetros especiais. Os parâmetros especiais são: 224 | * `Res/ResMut` para acessar recursos. 225 | * `Query` para acessar componentes de uma entidade. 226 | * `Commands` para criar e destruir entidades, componentes e recursos. 227 | * `EventWriter/EventReader` para enviar e receber eventos. 228 | 229 | Um sistema pode conter no máximo 16 parâmetros, caso seja preciso mais parâmetros pode se agrega-los em tuplas de no máximo 16 parâmetros. Caso estes limites não sejam suficiente, é possível fazer tuplas de tuplas. 230 | 231 | ```rust 232 | fn complex_system( 233 | (a, mut b): (Res, ResMut), 234 | (q0, q1, q2): (Query<(/* … */)>, Query<(/* … */)>, Query<(/* … */)>), 235 | ) { 236 | // lógica 237 | } 238 | ``` 239 | 240 | No sistema a cima `ResourceA` é um recurso imutável e esta compartilhando uma tupla com `ResourceB`que é um recurso mutável. Já `q0, q1 e q2` são componentes de uma entidade. 241 | 242 | Existem dois tipos de funções para executar sistemas na Bevy 243 | 244 | ```rust 245 | fn main() { 246 | App::new() 247 | .add_plugins(DefaultPlugins) 248 | // sistemas executados apenas quando o App é lançado 249 | .add_systems(Startup, (setup_camera, debug_start)) 250 | // sistemas executados todos os frames 251 | .add_systems(Update, (move_player, enemies_ai)) 252 | // ... 253 | .run(); 254 | } 255 | ``` 256 | 257 | Agora vamos começar a implementar nosso snake game e aprofundar nossos conhecimentos em bevy. 258 | 259 | **Referência: [unofficial bevy guide](https://bevy-cheatbook.github.io/programming.html)** 260 | -------------------------------------------------------------------------------- /src/part-2/05-spawnfood.md: -------------------------------------------------------------------------------- 1 | # Gerador de Comidas 2 | 3 | Nosso próximo passo é começarmos um sistema que gere comidas de forma aleatória pela grade. O primeiro passo é definir qual sera a cor da comida. Como pretendemos fazer um jogo multiplayer, não faz sentido termos comidas coloridas, já que estas serão dos jogadores, sendo assim podemos criar um módulo chamado `food` e adicionar a constante `const FOOD_COLOR: Color = Color::rgb(1.0, 1.0, 1.0)`. Próximo passo é criamos um componente chamado `Food` para representar a comida: 4 | 5 | ```rs 6 | // food.rs 7 | #[derive(Component)] 8 | pub struct Food; 9 | ``` 10 | 11 | Próximo passo é criarmos um sistema que gera uma comida em um local aleatório da grade. Como este sistema utiliza aleatoriedade, podemos utilizar uma biblioteca de `property testing` semelhante a *proptest* do python, a *propcheck* do Elixir e a *quickcheck* do Haskell, chamada `proptest` para gerar centenas de cenários de teste. Para isso, adicionamos `proptest = "1.4.0"` como uma `dev-dependencies` no Cargo.toml ou `cargo add proptest@1.4.0 --dev` na linha de comando e para utilizarmos basta utilizar a macro `proptest!` e determinar os valores a serem executados (ou quantidade de cenários) como argumento da função de teste como em `_execution in 0u16..1000`: 12 | 13 | ```rust 14 | #[cfg(test)] 15 | mod test { 16 | use crate::components::Position; 17 | 18 | use super::*; 19 | use proptest::prelude::*; 20 | 21 | proptest!{ 22 | #[test] 23 | fn spawns_food_inplace(_execution in 0u16..1000) { 24 | // Setup app 25 | let mut app = App::new(); 26 | 27 | // Add startup system 28 | app.add_startup_system(spawn_system); 29 | 30 | // Run systems 31 | app.update(); 32 | 33 | let mut query = app.world.query_filtered::<&Position, With>(); 34 | assert_eq!(query.iter(&app.world).count(), 1); 35 | query.iter(&app.world).for_each(|position| { 36 | let x = position.x; 37 | let y = position.y; 38 | 39 | assert!(x >= 0 && x as i16 <= (GRID_WIDTH -1) as i16); 40 | assert!(y >= 0 && y as i16 <= (GRID_HEIGHT -1) as i16); 41 | }) 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | Para usarmos nosso `GRID_WIDTH` e `GRID_HEIGHT` no nosso `food.rs` vamos precisar marcar nossa constante como uma crate publica pra isso precisamos fazer a modificação abaixo: 48 | ```rust 49 | // grid.rs 50 | pub(crate) const GRID_WIDTH: u16 = 10; 51 | pub(crate) const GRID_HEIGHT: u16 = 10; 52 | ``` 53 | 54 | A vantagem de um proptest é que ele permite executar diversos cenários e podemos definir regras de limite para falha, executando centenas de cenários em poucos segundos. Para este teste passar, precisamos implementar a função `spawn_system` para o módulo `food`: 55 | 56 | ```rust 57 | // food.rs 58 | use bevy::prelude::*; 59 | use rand::random; 60 | 61 | use crate::{ 62 | components::{Position, Size}, 63 | grid::{GRID_HEIGHT, GRID_WIDTH}, 64 | }; 65 | 66 | const FOOD_COLOR: Color = Color::rgb(1.0, 1.0, 1.0); 67 | 68 | #[derive(Component)] 69 | pub struct Food; 70 | 71 | pub fn spawn_system(mut commands: Commands) { 72 | commands 73 | .spawn(SpriteBundle { 74 | sprite: Sprite { 75 | color: FOOD_COLOR, 76 | ..default() 77 | }, 78 | ..default() 79 | }) 80 | .insert(Food) 81 | .insert(Position { 82 | x: (random::() % GRID_WIDTH) as i16, 83 | y: (random::() % GRID_HEIGHT) as i16, 84 | }) 85 | .insert(Size::square(0.8)); 86 | } 87 | 88 | #[cfg(test)] 89 | mod test { 90 | use crate::components::Position; 91 | 92 | use super::*; 93 | use proptest::prelude::*; 94 | 95 | proptest! { 96 | #[test] 97 | fn spawns_food_inplace(_execution in 0u16..1000) { 98 | // Setup app 99 | let mut app = App::new(); 100 | 101 | // Add startup system 102 | app.add_systems(Startup, spawn_system); 103 | 104 | // Run systems 105 | app.update(); 106 | 107 | let mut query = app.world.query_filtered::<&Position, With>(); 108 | assert_eq!(query.iter(&app.world).count(), 1); 109 | query.iter(&app.world).for_each(|position| { 110 | let x = position.x; 111 | let y = position.y; 112 | 113 | assert!(x >= 0 && x <= (GRID_WIDTH -1) as i16); 114 | assert!(y >= 0 && y <= (GRID_HEIGHT -1) as i16); 115 | }) 116 | } 117 | } 118 | } 119 | ``` 120 | 121 | O próximo passo é adicionar o sistema a `App` na função `main`, porém este sistema tem uma pegadinha. Como não queremos que o sistema gere uma nova comida para cada frame, precisamos definir um tempo de intervalo para as comidas serem geradas. Como este cenário de executar uma função somente a cada x segundos é muito comum no desenvolvimento de jogos a Bevy nos disponibiliza a struct `FixedTimestep` que nos permite definir um passo (`step`) em segundos, que será usada com a função `with_run_criteria`: 122 | 123 | ```rust 124 | use std::time::Duration; // <-- Import 125 | 126 | use bevy::{prelude::*, time::common_conditions::on_timer}; // <-- Import de on_timer 127 | 128 | pub mod components; 129 | pub mod food; 130 | pub mod grid; 131 | mod snake; 132 | 133 | fn main() { 134 | App::new() 135 | .add_systems(Startup, setup_camera) 136 | .add_systems(Startup, snake::spawn_system) 137 | .insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04))) 138 | .add_plugins( 139 | DefaultPlugins 140 | .set(WindowPlugin { 141 | primary_window: Some(Window { 142 | resolution: (500.0, 500.0).into(), 143 | title: "Snake".into(), 144 | resizable: false, 145 | ..default() 146 | }), 147 | ..default() 148 | }) 149 | .build(), 150 | ) 151 | .add_systems(PostUpdate, (grid::position_translation, grid::size_scaling)) 152 | .add_systems(Update, snake::movement_system) 153 | .add_systems( // <-- 154 | Update, // <-- Schedule 155 | food::spawn_system // <-- Sistema 156 | .run_if(on_timer(Duration::from_secs_f32(1.0))), //<-- Pegadinha 157 | ) 158 | .add_systems(PostUpdate, (grid::position_translation, grid::size_scaling)) 159 | .run(); 160 | } 161 | 162 | // código 163 | ``` 164 | 165 | Próximo passo será melhorar o movimento da cabeça da cobra, tornando ele mais lento e cadenciado. 166 | -------------------------------------------------------------------------------- /src/part-2/06-cadencia.md: -------------------------------------------------------------------------------- 1 | # Melhorando a Cadência do Movimento 2 | 3 | O atual movimento da cobra está ligado aos comandos do teclado diferentemente do snake game que a cobra se movimenta independente dos comandos do teclado e a cada x segundos, em vez de a cada frame. Para podermos fazer com que a cobra se movimente independente dos comandos do teclado, precisamos de uma forma de armazenar a direção que ela está se movimentando, além de evitar que a cobra vá para a direção oposta. Assim, precisamos criar o enum que armazena a direção e criar uma função que indica a direção oposta. 4 | 5 | ```rs 6 | // components.rs 7 | #[test] 8 | fn opposite_direction() { 9 | assert_eq!(Direction::Up.opposite(), Direction::Down); 10 | assert_eq!(Direction::Down.opposite(), Direction::Up); 11 | assert_eq!(Direction::Right.opposite(), Direction::Left); 12 | assert_eq!(Direction::Left.opposite(), Direction::Right); 13 | } 14 | 15 | // ... 16 | #[derive(PartialEq, Debug, Copy, Clone)] 17 | pub enum Direction { 18 | Left, 19 | Up, 20 | Right, 21 | Down, 22 | } 23 | 24 | impl Direction { 25 | pub fn opposite(self) -> Self { 26 | match self { 27 | Self::Left => Self::Right, 28 | Self::Right => Self::Left, 29 | Self::Up => Self::Down, 30 | Self::Down => Self::Up, 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | Em uma etapa inicial do desenvolvimento, eu adicionaria Direction como um componente na entidade cobra, porém, a medida que a cobra ficar maior, vai ser difícil sincronizar a direção de cada elemento. Sabendo disso, vamos fazer um pouco de overengineering, e colocar `Direction` como elemento do componente `snake::Head`. Além disso, definimos a direção padrão como `Direction::Up` utilizando a trait `Default`: 37 | 38 | ```rs 39 | // snake.rs 40 | 41 | #[derive(Component)] 42 | pub struct Head { 43 | direction: Direction 44 | } 45 | 46 | impl Default for Head { 47 | fn default() -> Self { 48 | Self { direction: Direction::Up } 49 | } 50 | } 51 | 52 | pub fn spawn_system(mut commands: Commands) { 53 | commands 54 | .spawn_bundle(SpriteBundle { 55 | sprite: Sprite { 56 | color: SNAKE_HEAD_COLOR, 57 | ..default() 58 | }, 59 | transform: Transform { 60 | scale: Vec3::new(10.0, 10.0, 10.0), 61 | ..default() 62 | }, 63 | ..default() 64 | }) 65 | .insert(Head::default() ) // <-- 66 | .insert(Position { x: 5, y: 5 }) 67 | .insert(Size::square(0.8)); 68 | } 69 | ``` 70 | 71 | ## Separando o movimento em duas etapas 72 | 73 | Agora que nossa cobra possui uma direção armazenada na cabeça podemos mudar seu sistema de movimento para que seja executado a cada 0.15 segundos, fazemos isso da mesma forma que fizemos com o sistema de geração de comidas: 74 | 75 | ```rs 76 | fn main() { 77 | App::new() 78 | .add_systems(Startup, setup_camera) 79 | .add_systems(Startup, snake::spawn_system) 80 | .insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04))) 81 | .add_plugins( 82 | DefaultPlugins 83 | .set(WindowPlugin { 84 | primary_window: Some(Window { 85 | resolution: (500.0, 500.0).into(), 86 | title: "Snake".into(), 87 | resizable: false, 88 | ..default() 89 | }), 90 | ..default() 91 | }) 92 | .build(), 93 | ) 94 | .add_systems(PostUpdate, (grid::position_translation, grid::size_scaling)) 95 | .add_systems(Update, snake::movement_system) 96 | .add_systems( 97 | Update, 98 | food::spawn_system.run_if(on_timer(Duration::from_secs_f32(1.0))), 99 | ) 100 | .add_systems( 101 | Update, 102 | snake::movement_system.run_if(on_timer(Duration::from_secs_f32(0.150))), 103 | ) 104 | .add_systems(PostUpdate, (grid::position_translation, grid::size_scaling)) 105 | .run(); 106 | } 107 | ``` 108 | 109 | Nosso sistema de movimento está atrelado à translação da cobra em uma posição para cada tecla que apertarmos e agora sabemos que o sistema de movimento deve ser independente do sistema de direção, que vamos chamar de `snake::movement_input_system`. Então precisamos que o sistema de input/direção aconteça antes do sistema de movimento, e, ainda, precisamos garantir que o sistema de movimento aconteça a cada `0.15` segundos. Para solucionar este problema, vamos adicionar uma função especial para sistemas `before`. Para utilizar está função, precisamos adicionar o sistema de input que vamos criar e indicar que ele deve ocorrer antes do sistema de movimento com `.add_systems(Update, snake::movement_input_system.before(snake::movement_system)`, adicione ao `App::new()` na função main. Agora vamos para o sistema `movement_input_system`. 110 | 111 | Primeira coisa que devemos fazer é alterar os testes para considerar que o `snake::movement_system` recebe uma query com `Position` e `snake::Head`, além de considerar que o movimento é feito com base no enum `Direction` contido dentro de `snake::Head`: 112 | 113 | 114 | ```rust 115 | // snake.rs 116 | #[test] 117 | fn snake_starts_moviment_up() { // <-- novo teste 118 | // Setup app 119 | let mut app = App::new(); 120 | 121 | // Add startup system 122 | app.add_systems(Startup, spawn_system); 123 | 124 | 125 | // Run systems 126 | app.update(); 127 | 128 | let mut query = app.world.query::<&Head>(); 129 | let head = query.iter(&app.world).next().unwrap(); 130 | assert_eq!(head.direction, Direction::Up); 131 | } 132 | 133 | #[test] 134 | fn snake_head_has_moved_up() { 135 | // Setup 136 | let mut app = App::new(); 137 | let default_position = Position { x: 5, y: 6 }; 138 | 139 | // Adicionando sistemas 140 | app.add_systems(Startup, spawn_system) 141 | .add_systems(Update, movement_system) 142 | .add_systems(Update, movement_input_system.before(movement_system)); 143 | 144 | 145 | // Adicionando inputs de `KeyCode`s 146 | let mut input = Input::::default(); 147 | input.press(KeyCode::W); 148 | app.insert_resource(input); 149 | 150 | // Executando sistemas pelo menos uma vez 151 | app.update(); 152 | 153 | //Assert 154 | let mut query = app.world.query::<(&Head, &Position)>(); 155 | query.iter(&app.world).for_each(|(head, position)| { 156 | assert_eq!(&default_position, position); 157 | assert_eq!(head.direction, Direction::Up); // <-- novo assert 158 | 159 | }) 160 | } 161 | 162 | #[test] 163 | fn snake_head_moves_up_and_right() { 164 | // Setup 165 | let mut app = App::new(); 166 | let up_position = Position { x: 5, y: 6 }; 167 | 168 | // Adiciona systemas 169 | app.add_systems(Startup, spawn_system) 170 | .add_systems(Update, movement_system) 171 | .add_systems(Update, movement_input_system.before(movement_system)); 172 | 173 | // Testa movimento para cima 174 | let mut input = Input::::default(); 175 | input.press(KeyCode::W); 176 | app.insert_resource(input); 177 | app.update(); 178 | 179 | let mut query = app.world.query::<(&Head, &Position)>(); 180 | query.iter(&app.world).for_each(|(head, position)| { 181 | assert_eq!(position, &up_position); 182 | assert_eq!(head.direction, Direction::Up); // <- Novo assert 183 | }); 184 | 185 | let up_right_position = Position { x: 6, y: 6 }; 186 | 187 | // Testa movimento para direita 188 | let mut input = Input::::default(); 189 | input.press(KeyCode::D); 190 | app.insert_resource(input); 191 | app.update(); 192 | 193 | let mut query = app.world.query::<(&Head, &Position)>(); 194 | query.iter(&app.world).for_each(|(head, position)| { 195 | assert_eq!(&up_right_position, position); 196 | assert_eq!(head.direction, Direction::Right); // <- Novo assert 197 | 198 | }) 199 | } 200 | 201 | 202 | #[test] 203 | fn snake_head_moves_down_and_left() { 204 | // Setup 205 | let mut app = App::new(); 206 | let down_left_position = Position { x: 4, y: 6 }; 207 | 208 | // Add systems 209 | app.add_startup_system(spawn_system) 210 | .add_system(movement_system) 211 | .add_system(movement_input_system.before(movement_system)); // <-- 212 | 213 | // Move Left 214 | let mut input = Input::::default(); 215 | input.press(KeyCode::A); 216 | app.insert_resource(input); 217 | app.update(); 218 | 219 | // Move down 220 | let mut input = Input::::default(); 221 | input.press(KeyCode::S); 222 | app.insert_resource(input); 223 | app.update(); 224 | 225 | // Assert 226 | let mut query = app.world.query::<(&Head, &Position)>(); 227 | query.iter(&app.world).for_each(|(head, position)| { 228 | assert_eq!(&down_left_position, position); 229 | assert_eq!(head.direction, Direction::Left); 230 | }) 231 | } 232 | #[test] 233 | fn snake_cannot_start_moving_down() { // <-- novo teste 234 | // Setup 235 | let mut app = App::new(); 236 | let down_left_position = Position { x: 5, y: 6 }; 237 | 238 | // Add systems 239 | app.add_systems(Startup, spawn_system) 240 | .add_systems(Update, movement_system) 241 | r .add_systems(Update, movement_input_system.before(movement_system)); 242 | 243 | // Move down 244 | let mut input = Input::::default(); 245 | input.press(KeyCode::S); 246 | app.insert_resource(input); 247 | app.update(); 248 | 249 | // Assert 250 | let mut query = app.world.query::<(&Head, &Position)>(); 251 | query.iter(&app.world).for_each(|(_head, position)| { 252 | assert_eq!(&down_left_position, position); 253 | }) 254 | } 255 | 256 | ``` 257 | 258 | > Foram criados dois novos testes: `snake_starts_moviment_up` que checa se a cobra inicia seu movimento para cima. `snake_cannot_start_moving_down` checa que não é possível se movimentar na direção oposta. 259 | 260 | Com estes testes sabemos que o movement system agora deve receber uma query com a posição mutável e com a `Head` para obtermos a direção. Com base na direção, movemos a posição da cabeça: 261 | 262 | ```rust 263 | pub fn movement_system(mut heads: Query<(&mut Position, &Head)>) { 264 | if let Some((mut pos, head)) = heads.iter_mut().next() { 265 | match &head.direction { 266 | Direction::Left => { 267 | pos.x -= 1; 268 | } 269 | Direction::Right => { 270 | pos.x += 1; 271 | } 272 | Direction::Up => { 273 | pos.y += 1; 274 | } 275 | Direction::Down => { 276 | pos.y -= 1; 277 | } 278 | }; 279 | } 280 | } 281 | ``` 282 | 283 | Já o `movement_input_system` recebe uma leitura de teclado (`KeyCode`) e muda a direção com base nesta leitura do teclado e evita mudanças na direção oposta: 284 | 285 | ```rs 286 | pub fn movement_input_system( 287 | keyboard_input: Res>, 288 | mut heads: Query<&mut Head>) { 289 | if let Some(mut head) = heads.iter_mut().next() { 290 | let dir: Direction = if keyboard_input.pressed(KeyCode::A) { 291 | Direction::Left 292 | } else if keyboard_input.pressed(KeyCode::S) { 293 | Direction::Down 294 | } else if keyboard_input.pressed(KeyCode::W) { 295 | Direction::Up 296 | } else if keyboard_input.pressed(KeyCode::D) { 297 | Direction::Right 298 | } else { 299 | head.direction 300 | }; 301 | if dir != head.direction.opposite() { 302 | head.direction = dir; 303 | } 304 | } 305 | } 306 | ``` 307 | 308 | Ao executarmos `cargo run` podemos ver a cobra se movimentando sozinha e obedecendo o sistema de direção. Próximo passo é adicionarmos o rabo. 309 | -------------------------------------------------------------------------------- /src/part-2/09-migrando-versoes.md: -------------------------------------------------------------------------------- 1 | # Migrando versões da Bevy 2 | 3 | A equipe da Bevy fez um trabalho sensacional auxiliando equipes que usam a engine a manter seus códigos atulizados com as novas versões e guias futuros podem ser encontrados em https://bevyengine.org/learn/book/migration-guides/. Neste momento a última versão é a 0.12, portanto adicionaremos as alterações das migrações abaixo. 4 | 5 | Caso ocorra alguma incompatibilidade com uma versão nova, por favor abra uma [issue](https://github.com/naomijub/Rust-game-dev/issues) ou um PR nos repositórios do github [livro](https://github.com/naomijub/Rust-game-dev) e [codigo](https://github.com/naomijub/bevy-snake). 6 | -------------------------------------------------------------------------------- /src/part-3/00-capa.md: -------------------------------------------------------------------------------- 1 | # Multiplayer Online 2 | 3 | **ESTE CAPÍTULO SERÁ INICIADO SOMENTE APÓS OUTROS PROJETOS FOREM CONCLUÍDOS** (~Sem prazo :< ) 4 | --------------------------------------------------------------------------------
This URL is invalid, sorry. Please use the navigation bar or search to continue.