├── .eslintignore
├── .eslintrc.js
├── .github
├── FUNDING.yml
└── workflows
│ └── tests.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierignore
├── .prettierrc.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cert.pem
├── docs
├── api_docs
│ ├── button.js
│ ├── gameObject.js
│ ├── grid.js
│ ├── plugin.js
│ ├── pool.js
│ ├── quadtree.js
│ ├── sprite.js
│ └── tileEngine.js
├── assets
│ ├── data
│ │ ├── tile_engine_add.json
│ │ ├── tile_engine_add.tmx
│ │ ├── tile_engine_basic.json
│ │ ├── tile_engine_basic.tmx
│ │ ├── tile_engine_camera.json
│ │ └── tile_engine_camera.tmx
│ ├── imgs
│ │ ├── Attribution.txt
│ │ ├── blue_button02.png
│ │ ├── blue_button03.png
│ │ ├── character.png
│ │ ├── character_walk_sheet.png
│ │ ├── games
│ │ │ ├── 20461-dioretsa.jpg
│ │ │ ├── a-day-in-the-life.jpg
│ │ │ ├── an-offline-life.jpg
│ │ │ ├── audio-dash.jpg
│ │ │ ├── back-down-the-tower.jpg
│ │ │ ├── back-forth.jpg
│ │ │ ├── back-in-dino.jpg
│ │ │ ├── back-on-track.jpg
│ │ │ ├── back-to-bathroom.jpg
│ │ │ ├── back-to-life.jpg
│ │ │ ├── back-to-that-platform-i-was-on-before-jumping.jpg
│ │ │ ├── back-to-the-island.jpg
│ │ │ ├── back-to-the-nest.jpg
│ │ │ ├── backside.jpg
│ │ │ ├── backstabber-hero.jpg
│ │ │ ├── backstone.jpg
│ │ │ ├── barry-the-bird.jpg
│ │ │ ├── blackout.jpg
│ │ │ ├── bubble-burst.png
│ │ │ ├── captain-katamov.jpg
│ │ │ ├── cat-goric-escape-from-the-warp-chamber.png
│ │ │ ├── close-to-me.jpg
│ │ │ ├── detro.jpg
│ │ │ ├── dodge-that-shit.jpg
│ │ │ ├── earth-that-was.jpg
│ │ │ ├── eat-my-dust.jpg
│ │ │ ├── flight-back-home.jpg
│ │ │ ├── friendly-alien.jpg
│ │ │ ├── frog-jumper.png
│ │ │ ├── godai-is-back.jpg
│ │ │ ├── grow-back.jpg
│ │ │ ├── hang-by-a-thread.jpeg
│ │ │ ├── i-forgot-my-sword.jpeg
│ │ │ ├── kontra-game-samples.jpg
│ │ │ ├── kurve-space.jpg
│ │ │ ├── lost-pages.jpg
│ │ │ ├── lost-robot.jpg
│ │ │ ├── meadow.jpg
│ │ │ ├── mongol-march.png
│ │ │ ├── ninja-take-back.jpg
│ │ │ ├── noegnud.jpg
│ │ │ ├── population-404.png
│ │ │ ├── send-the-asteroids-back.jpg
│ │ │ ├── shoot2live.jpg
│ │ │ ├── snek-farm.png
│ │ │ ├── start-over.jpg
│ │ │ ├── tanky-mctankface.jpg
│ │ │ ├── the-waffle.jpg
│ │ │ ├── they-follow.jpg
│ │ │ ├── toe-tac-tic.jpg
│ │ │ ├── trench-fisher.jpeg
│ │ │ ├── troposphere.jpg
│ │ │ └── we-must-go-back.jpg
│ │ └── mapPack_tilesheet.png
│ ├── js
│ │ ├── exampleTabList.js
│ │ ├── navbar.js
│ │ ├── pool.js
│ │ ├── quadtree.js
│ │ └── snake.js
│ └── styles.css
├── pages
│ ├── download.js
│ ├── getting-started.js
│ ├── index.js
│ ├── made-with-kontra.js
│ ├── reducing-file-size.js
│ └── tutorials.js
├── service-worker.js
└── template
│ ├── partials
│ ├── example-output.hbs
│ └── parameter-table.hbs
│ └── template.hbs
├── examples
├── audio
│ ├── Attribution.txt
│ ├── Digital_Forest.mp3
│ └── Digital_Forest.ogg
├── button
│ ├── button.html
│ ├── buttonImage.html
│ └── buttonStates.html
├── event
│ └── assetLoaded.html
├── galaxian
│ ├── imgs
│ │ ├── bg.png
│ │ ├── bullet.png
│ │ ├── bullet_enemy.png
│ │ ├── enemy.png
│ │ └── ship.png
│ ├── index.html
│ ├── js
│ │ ├── boot.js
│ │ ├── galaxian.js
│ │ ├── gameOver.js
│ │ └── index.js
│ └── sounds
│ │ ├── explosion.mp3
│ │ ├── game_over.mp3
│ │ ├── kick_shock.mp3
│ │ └── laser.mp3
├── gamepad
│ ├── gamepad.html
│ └── multiplayer.html
├── gesture
│ └── gesture.html
├── grid
│ └── optionMenu.html
├── imgs
│ ├── Attribution.txt
│ ├── blue_button02.png
│ ├── bulletBlue1.png
│ ├── bulletGreen1.png
│ ├── bulletRed1.png
│ ├── bulletSand1.png
│ ├── character.png
│ ├── character_walk_sheet.png
│ ├── oldHero.png
│ ├── tank_blue.png
│ ├── tank_green.png
│ ├── tank_red.png
│ └── tank_sand.png
├── pointer
│ ├── pointer.html
│ ├── touchEvents.html
│ └── tracking.html
├── pool
│ └── particleEngine.html
├── prism
│ └── codeOutput.js
├── scene
│ ├── depthSortObjects.html
│ ├── gameScene.js
│ ├── globals.js
│ ├── menuScene.js
│ ├── simpleScenes.html
│ └── snake.js
├── sprite
│ ├── animationSprite.html
│ ├── clampSpriteMovement.html
│ ├── controllingASprite.html
│ ├── customUpdateAndDraw.html
│ ├── extendingASprite.html
│ ├── imageSprite.html
│ ├── movingASprite.html
│ ├── rectSprite.html
│ └── spriteCollision.html
├── spriteSheet
│ └── margin.html
├── text
│ ├── text.html
│ ├── textAlign.html
│ ├── textAutoNewline.html
│ ├── textLineHeight.html
│ └── textNewline.html
└── tileEngine
│ ├── camera
│ ├── Attribution.txt
│ ├── chicken_eat.png
│ ├── index.html
│ ├── man.png
│ ├── terrain.json
│ ├── terrain.png
│ └── terrain.tmx
│ └── margin
│ ├── index.html
│ ├── marginTiles.json
│ ├── marginTiles.tmx
│ ├── roguelikeDungeon_transparent.json
│ ├── roguelikeDungeon_transparent.png
│ └── roguelikeDungeon_transparent.tsx
├── gulpfile.js
├── karma.conf.js
├── key.pem
├── package-lock.json
├── package.json
├── src
├── animation.js
├── assets.js
├── button.js
├── core.js
├── events.js
├── gameLoop.js
├── gameObject.js
├── gamepad.js
├── gesture.js
├── grid.js
├── helpers.js
├── input.js
├── keyboard.js
├── kontra.defaults.js
├── kontra.js
├── plugin.js
├── pointer.js
├── pool.js
├── quadtree.js
├── random.js
├── scene.js
├── sprite.js
├── spriteSheet.js
├── text.js
├── tileEngine.js
├── updatable.js
├── utils.js
└── vector.js
├── tasks
├── docs.js
├── release.sh
├── ts-template.hbs
└── typescript.js
└── test
├── audio
├── shoot.mp3
└── shoot.ogg
├── data
├── source.json
├── test.json
├── test.txt
└── tileset
│ ├── bullet.png
│ ├── readme.txt
│ └── tileset.json
├── imgs
└── bullet.png
├── integration
├── core.spec.js
├── scene.spec.js
├── sprite.spec.js
├── tileEngine.spec.js
└── vector.spec.js
├── permutations
├── index.js
└── karma.conf.template.js
├── setup.js
├── typings
├── animation.ts
├── assets.ts
├── button.ts
├── core.ts
├── events.ts
├── gameLoop.ts
├── gameObject.ts
├── gamepad.ts
├── gesture.ts
├── grid.ts
├── helpers.ts
├── input.ts
├── keyboard.ts
├── pointer.ts
├── pool.ts
├── quadtree.ts
├── random.ts
├── scene.ts
├── sprite.ts
├── spriteSheet.ts
├── text.ts
├── tileEngine.ts
└── vector.ts
├── unit
├── animation.spec.js
├── assets.spec.js
├── button.spec.js
├── core.spec.js
├── events.spec.js
├── gameLoop.spec.js
├── gameObject.spec.js
├── gamepad.spec.js
├── gesture.spec.js
├── grid.spec.js
├── helpers.spec.js
├── input.spec.js
├── keyboard.spec.js
├── kontra.defaults.spec.js
├── kontra.spec.js
├── plugin.spec.js
├── pointer.spec.js
├── pool.spec.js
├── quadtree.spec.js
├── random.spec.js
├── scene.spec.js
├── sprite.spec.js
├── spriteSheet.spec.js
├── text.spec.js
├── tileEngine.spec.js
├── updatable.spec.js
├── utils.spec.js
└── vector.spec.js
└── utils.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | docs/*
3 | examples/*
4 | test/typings/*.js
5 | test/permutations/*
6 | tasks/*
7 |
8 | gulpfile.js
9 | karma.conf.js
10 | kontra.*
11 | .eslintrc.js
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true
5 | },
6 | extends: 'eslint:recommended',
7 | parserOptions: {
8 | ecmaVersion: 13,
9 | sourceType: 'module'
10 | },
11 | rules: {
12 | // byte saving rules
13 | 'no-duplicate-imports': 2,
14 | 'object-shorthand': 2,
15 | 'prefer-arrow-callback': 2,
16 | 'no-else-return': 2,
17 | 'one-var': [
18 | 'error',
19 | {
20 | uninitialized: 'always'
21 | }
22 | ],
23 | 'prefer-exponentiation-operator': 2,
24 | 'no-restricted-syntax': [
25 | 'error',
26 | {
27 | selector: 'BinaryExpression[operator="==="]',
28 | message: "Prefer '==' over '===' to save bytes"
29 | },
30 | {
31 | selector: 'BinaryExpression[operator="!=="]',
32 | message: "Prefer '!=' over '!==' to save bytes"
33 | },
34 | {
35 | selector:
36 | 'MemberExpression[object.name=Math][property.name=floor]',
37 | message: "Prefer '| 0' over 'Math.floor' to save bytes"
38 | },
39 | {
40 | selector: 'VariableDeclaration[kind=const]',
41 | message: "Prefer 'let' over 'const' to save bytes"
42 | },
43 | {
44 | selector: 'MemberExpression[property.name=forEach]',
45 | message: "Prefer '.map()' over '.forEach()' to save bytes"
46 | }
47 | ],
48 |
49 | // code style rules
50 | 'spaced-comment': [
51 | 'error',
52 | 'always',
53 | {
54 | exceptions: ['@__PURE__']
55 | }
56 | ],
57 | 'array-bracket-spacing': 2,
58 | 'object-curly-spacing': ['error', 'always'],
59 | 'no-trailing-spaces': 2,
60 | 'multiline-comment-style': ['error', 'separate-lines'],
61 | 'max-len': [
62 | 'error',
63 | {
64 | comments: 70,
65 | // ignore JSDoc block comments, @ifdef comments, template
66 | // literals with placeholders (ignoreStrings option doesn't
67 | // seem to work with it), and mocha describe and it functions
68 | // with template literals with placeholders
69 | ignorePattern:
70 | '^\\s*(\\*|\\/\\/ @ifdef|\\/\\* @ifdef|`|describe\\(`)|it\\(`',
71 | ignoreTrailingComments: true,
72 | ignoreUrls: true,
73 | ignoreStrings: true,
74 | ignoreRegExpLiterals: true
75 | }
76 | ]
77 | },
78 | overrides: [
79 | {
80 | files: ['test/**/*.js'],
81 | env: {
82 | browser: true,
83 | es2021: true,
84 | mocha: true
85 | },
86 | globals: {
87 | expect: true,
88 | sinon: true
89 | },
90 | plugins: ['mocha-no-only'],
91 | rules: {
92 | 'no-restricted-syntax': 0,
93 | 'mocha-no-only/mocha-no-only': 2
94 | }
95 | }
96 | ]
97 | };
98 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: straker
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | coverage
4 | docs/api
5 | docs/**/*.html
6 | docs/assets/js/kontra.js
7 | kontra.*
8 | test/permutations/*.js
9 | test/typings/*.js
10 | test/playground.html
11 | tmp
12 |
13 | !test/permutations/index.js
14 | !test/permutations/karma.conf.template.js
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "avoid",
3 | "bracketSpacing": true,
4 | "endOfLine": "lf",
5 | "htmlWhitespaceSensitivity": "css",
6 | "insertPragma": false,
7 | "jsxBracketSameLine": false,
8 | "jsxSingleQuote": false,
9 | "printWidth": 70,
10 | "proseWrap": "preserve",
11 | "quoteProps": "as-needed",
12 | "requirePragma": false,
13 | "semi": true,
14 | "singleQuote": true,
15 | "tabWidth": 2,
16 | "trailingComma": "none",
17 | "useTabs": false,
18 | "vueIndentScriptAndStyle": false,
19 | "parser": "babel"
20 | }
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to make participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies within all project spaces, and it also applies when
49 | an individual is representing the project or its community in public spaces.
50 | Examples of representing a project or community include using an official
51 | project e-mail address, posting via an official social media account, or acting
52 | as an appointed representative at an online or offline event. Representation of
53 | a project may be further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at steven@sklambert.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Steven Lambert
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://badge.fury.io/js/kontra)
2 | [](https://github.com/straker/kontra/actions)
3 |
4 | # Kontra.js
5 |
6 | A lightweight JavaScript gaming micro-library, optimized for js13kGames.
7 |
8 | ## Documentation
9 |
10 | All the documentation can be found on the [github page](https://straker.github.io/kontra/).
11 |
12 | ## Community
13 |
14 | If you'd like to chat about Kontra, check out the `#kontra` channel of either [the js13kGames Slack](https://slack.js13kgames.com/) or [the Gamedev.js Discord](https://discord.gg/URWvCwv)!
15 |
16 | ## Contributing
17 |
18 | See the [Contributing file](CONTRIBUTING.md).
19 |
20 | ## Supporting
21 |
22 | Kontra.js is made possible by users like you. Through helping find issues, opening pull requests, and [funding continuous development](https://www.patreon.com/straker), Kontra.js can continue to provide you with quality improvements and updates.
23 |
24 | When you become a Patron, you get access to behind the scenes development logs, the ability to vote on which features to work on next, and early access to development builds.
25 |
26 | ### Top Patrons
27 |
28 | - Karar Al-Remahy
29 | - UnbrandedTech
30 |
--------------------------------------------------------------------------------
/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDqDCCApCgAwIBAgIJALpeHCqCkSB/MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
3 | BAYTAlVTMQ0wCwYDVQQIDARVdGFoMRkwFwYDVQQHDBBTYXJhdG9nYSBTcHJpbmdz
4 | MRcwFQYDVQQKDA5TdGV2ZW4gTGFtYmVydDEXMBUGA1UEAwwOU3RldmVuIExhbWJl
5 | cnQwHhcNMjEwMTMwMjIyMjU0WhcNMzEwMTI4MjIyMjU0WjBpMQswCQYDVQQGEwJV
6 | UzENMAsGA1UECAwEVXRhaDEZMBcGA1UEBwwQU2FyYXRvZ2EgU3ByaW5nczEXMBUG
7 | A1UECgwOU3RldmVuIExhbWJlcnQxFzAVBgNVBAMMDlN0ZXZlbiBMYW1iZXJ0MIIB
8 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Nvp03b7D6wIlPKVpEsXYoRm
9 | cwbBRo6GkMgtsdedq9RXtneyYym/rzIywa292f63bFBgWuBWnmJMMluh5pxHJBCA
10 | EpiEiVNFBxMMrcsKFPS3FrPcHwht1USVeCFYa8egmdNScxrpJC4zt1x6tB1vpnS+
11 | ge0GFDDPCKnhmEtbtdioX97GVbJcV/RQNE8MkuPCvf608OxnXkGNL/vfaznyXYBC
12 | bTn7+fVCIOAhhvd+FXNKOMQ78hU9Lc/FMf4YjmDoQUjm9xUiQfujYMV/thSfzmNg
13 | xPbKLe5om2gmC9LDhBfRYu+pe5RRW3MyH3H/lnL1SdVMe4vsKmcu/t9gZMBxcQID
14 | AQABo1MwUTAdBgNVHQ4EFgQUkS3L8tQNyqPOr3++5NTkp2s464UwHwYDVR0jBBgw
15 | FoAUkS3L8tQNyqPOr3++5NTkp2s464UwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
16 | 9w0BAQsFAAOCAQEAq6QgEimVS61WLrkiRdS5mI8JgyEsUGZmmaF7QBdunckiQSfo
17 | 4qdcF7G4iQXwJk+c9oRW1KBQ180VCj2EsRHsX/nGxuWltis8EZTxVBYDmtpZu/Zt
18 | QzMspRbpUZoqqeSPYywssXVmqyV2FpGR84lyFZuyacM2Xfhwhyn6M8X6e6JUL1wB
19 | 9JM57NnsMVUSdgTuy/OpUl4TZXzJiVRtRnhtlZdbeJdQ+9AQ999u5bsZSShDlUEk
20 | GqnFHmKFOyjJM4ytFATITng1sdAd/LrzyyMENBH1sy5Qz+u3qUMOiZkxtsRH7gTt
21 | 7hFJsHn0snkJT6fKdP8iVyxswmeGbkcrExZmNg==
22 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/docs/api_docs/gameObject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A GameObject is just a base class and typically isn't used directly. Instead, it's main purpose is to be extended by other classes, such as [Sprite](api/sprite).
3 | *
4 | * To extend the GameObject class, import the underlying class as `import { GameObjectClass }`.
5 | *
6 | * You should also override the `draw()` function instead of the `render()` function in your class. The `draw()` function determines how to draw the GameObject. It is called by the `render` function after transforms and rotations have been applied.
7 | *
8 | * Do note that the canvas has been rotated and translated to the objects position (taking into account anchor), so {0,0} will be the top-left corner of the game object when drawing.
9 | *
10 | * @sectionName Extending A GameObject
11 | * @example
12 | * // exclude-code:start
13 | * let { GameObjectClass } = kontra;
14 | * // exclude-code:end
15 | * // exclude-script:start
16 | * import { GameObjectClass } from 'kontra';
17 | * // exclude-script:end
18 | *
19 | * class Triangle extends GameObjectClass {
20 | * constructor(properties) {
21 | * super(properties);
22 | * }
23 | *
24 | * draw() {
25 | * this.context.fillStyle = this.color;
26 | * this.context.beginPath();
27 | * this.context.moveTo(0, 0);
28 | * this.context.lineTo(this.width, 0);
29 | * this.context.lineTo(this.width / 2, this.height);
30 | * this.context.fill();
31 | * }
32 | * }
33 | *
34 | * let triangle = new Triangle({
35 | * x: 300,
36 | * y: 100,
37 | * width: 30,
38 | * height: 40,
39 | * anchor: {x: 0.5, y: 0.5},
40 | * color: 'red'
41 | * });
42 | * // exclude-code:start
43 | * triangle.context = context;
44 | * // exclude-code:end
45 | * triangle.render();
46 | */
47 |
--------------------------------------------------------------------------------
/docs/api_docs/grid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The grid automatically positions each object based on the grids [flow](api/grid#flow). This means you do not need to set the x/y position of any of the objects and can let the grid handle that for you. This makes it extremely easy to set up UI elements such as menus without having to hand place each one.
3 | *
4 | * @sectionName Basic Use
5 | * @example
6 | * // exclude-code:start
7 | * let { Text, Grid } = kontra;
8 | * // exclude-code:end
9 | * // exclude-script:start
10 | * import { Text, Grid } from 'kontra';
11 | * // exclude-script:end
12 | *
13 | * let textOptions = {
14 | * color: 'white',
15 | * font: '20px Arial, sans-serif'
16 | * };
17 | *
18 | * let start = Text({
19 | * text: 'Start',
20 | * ...textOptions
21 | * });
22 | *
23 | * let options = Text({
24 | * text: 'Options',
25 | * ...textOptions
26 | * });
27 | *
28 | * let quit = Text({
29 | * text: 'Quit',
30 | * ...textOptions
31 | * });
32 | *
33 | * let menu = Grid({
34 | * x: 300,
35 | * y: 100,
36 | * anchor: {x: 0.5, y: 0.5},
37 | * // exclude-code:start
38 | * context: context,
39 | * // exclude-code:end
40 | *
41 | * // add 15 pixels of space between each row
42 | * rowGap: 15,
43 | *
44 | * // center the children
45 | * justify: 'center',
46 | *
47 | * children: [start, options, quit]
48 | * });
49 | *
50 | * menu.render();
51 | */
52 |
53 | /**
54 | * The grid supports properties on the child objects that change how the child is positioned within the grid.
55 | *
56 | * - `alignSelf` - *String*. Align this item individually in the grid, overriding the grids [align](api/grid#align) setting.
57 | * - `justifySelf` - *String*. Justify this item individually in the grid, overriding the grids [justify](api/grid#justify) setting.
58 | * - `colSpan` - *Number*. Have the item take up more than 1 column. Great for menu titles.
59 | *
60 | * @sectionName Child Properties
61 | */
62 |
--------------------------------------------------------------------------------
/docs/api_docs/pool.js:
--------------------------------------------------------------------------------
1 | /**
2 | * To use the pool, you must pass the `create()` function argument. The create function should return a new instance of an object or [Sprite](api/sprite). This object will be added to the pool every time there are no more alive objects.
3 | *
4 | * The object must implement the functions `update()`, `render()`, `init()`, and `isAlive()`. If one of these functions is missing the pool will throw an error. [Sprite](api/sprite) defines these functions for you.
5 | *
6 | * An object is available for reuse when its `isAlive()` function returns `false`. For a sprite, this is typically when its ttl is `0`.
7 | *
8 | * When you want an object from the pool, use the pools [get()](api/pool/#get) function and pass it any properties you want the newly initialized object to have.
9 | *
10 | * ```js
11 | * import { Pool, Sprite } from 'kontra';
12 | *
13 | * let pool = Pool({
14 | * // create a new sprite every time the pool needs a new object.
15 | * // equivalent to `create(props) { return Sprite(props) }`
16 | * create: Sprite
17 | * });
18 | *
19 | * // properties will be passed to the sprites init() function
20 | * pool.get({
21 | * x: 100,
22 | * y: 200,
23 | * width: 20,
24 | * height: 40,
25 | * color: 'red',
26 | * ttl: 60
27 | * });
28 | * ```
29 | *
30 | * When you want to update or render all alive objects in the pool, use the pools [update()](api/pool/#update) and [render()](api/pool/#render) functions.
31 | *
32 | * ```js
33 | * import { GameLoop } from 'kontra';
34 | *
35 | * let loop = GameLoop({
36 | * update: function() {
37 | * pool.update();
38 | * },
39 | * render: function() {
40 | * pool.render();
41 | * }
42 | * });
43 | * ```
44 | * @sectionName Basic Use
45 | */
46 |
--------------------------------------------------------------------------------
/docs/api_docs/quadtree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Every frame you should remove all objects from the quadtree using its [clear()](api/quadtree/#clear) function and then add all objects back using its [add()](api/quadtree/#add) function. You can add a single object, a list of objects, or an array of objects.
3 | *
4 | * ```js
5 | * import { Quadtree, Sprite, GameLoop } from 'kontra';
6 | *
7 | * let quadtree = Quadtree();
8 | * let player = Sprite({
9 | * // ...
10 | * });
11 | * let enemy = Sprite({
12 | * // ...
13 | * });
14 | *
15 | * let loop = GameLoop({
16 | * update: function() {
17 | * quadtree.clear();
18 | * quadtree.add(player, enemy);
19 | * }
20 | * });
21 | * ```
22 | *
23 | * You should clear the quadtree each frame as the quadtree is only a snapshot of the position of the objects when they were added. Since the quadtree doesn't know anything about those objects, it doesn't know when an object moved or when it should be removed from the tree.
24 | *
25 | * Objects added to the tree must have the properties `x`, `y`, `width`, and `height` so that their position in the quadtree can be calculated. [Sprite](api/sprite) defines these properties for you.
26 | *
27 | * When you need to get all objects in the same node as another object, use the quadtrees [get()](api/quadtree#get) function.
28 | *
29 | * ```js
30 | * // exclude-tablist
31 | * let objects = quadtree.get(player); //=> [enemy]
32 | * ```
33 | * @sectionName Basic Use
34 | */
35 |
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_add.json:
--------------------------------------------------------------------------------
1 | { "height":9,
2 | "infinite":false,
3 | "layers":[
4 | {
5 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 204, 204, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 204, 204, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 204, 204, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 204, 204, 0, 0, 0, 0, 0, 0, 0, 204, 204, 204, 204, 204, 204],
6 | "height":9,
7 | "id":9,
8 | "name":"water",
9 | "opacity":1,
10 | "type":"tilelayer",
11 | "visible":true,
12 | "width":13,
13 | "x":0,
14 | "y":0
15 | },
16 | {
17 | "data":[29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 14, 46, 46, 15, 29, 29, 29, 29, 29, 29, 29, 29, 14, 47, 11, 13, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 11, 32, 30, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 45, 46, 47, 28, 29, 29, 29, 29, 29, 29, 29, 29, 31, 12, 12, 12, 32],
18 | "height":9,
19 | "id":10,
20 | "name":"ground",
21 | "opacity":1,
22 | "type":"tilelayer",
23 | "visible":true,
24 | "width":13,
25 | "x":0,
26 | "y":0
27 | },
28 | {
29 | "data":[49, 49, 49, 139, 143, 177, 143, 141, 143, 143, 143, 174, 49, 49, 49, 49, 142, 49, 49, 49, 142, 49, 49, 49, 142, 49, 143, 177, 143, 194, 49, 49, 49, 193, 143, 143, 143, 191, 49, 49, 49, 49, 142, 49, 49, 49, 142, 49, 49, 49, 49, 49, 49, 49, 49, 193, 143, 177, 143, 157, 49, 49, 0, 0, 49, 49, 0, 0, 142, 0, 49, 49, 49, 0, 0, 0, 0, 49, 0, 0, 48, 176, 48, 197, 143, 175, 143, 177, 143, 180, 49, 0, 48, 48, 142, 48, 48, 0, 142, 0, 0, 0, 0, 49, 0, 48, 48, 156, 143, 177, 143, 157, 0, 0, 0, 0, 49],
30 | "height":9,
31 | "id":11,
32 | "name":"path",
33 | "opacity":1,
34 | "type":"tilelayer",
35 | "visible":true,
36 | "width":13,
37 | "x":0,
38 | "y":0
39 | },
40 | {
41 | "data":[0, 0, 0, 147, 0, 0, 0, 148, 0, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 0, 0, 0, 164, 0, 0, 0, 0, 0],
42 | "height":9,
43 | "id":12,
44 | "name":"places",
45 | "opacity":1,
46 | "type":"tilelayer",
47 | "visible":true,
48 | "width":13,
49 | "x":0,
50 | "y":0
51 | }],
52 | "nextlayerid":13,
53 | "nextobjectid":1,
54 | "orientation":"orthogonal",
55 | "renderorder":"right-down",
56 | "tiledversion":"1.2.1",
57 | "tileheight":64,
58 | "tilesets":[
59 | {
60 | "columns":17,
61 | "firstgid":1,
62 | "image":"..\/imgs\/mapPack_tilesheet.png",
63 | "imageheight":768,
64 | "imagewidth":1088,
65 | "margin":0,
66 | "name":"mapPack_tilesheet",
67 | "spacing":0,
68 | "tilecount":204,
69 | "tileheight":64,
70 | "tilewidth":64
71 | }],
72 | "tilewidth":64,
73 | "type":"map",
74 | "version":1.2,
75 | "width":13
76 | }
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_add.tmx:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_basic.json:
--------------------------------------------------------------------------------
1 | { "height":9,
2 | "layers":[
3 | {
4 | "data":[203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203],
5 | "height":9,
6 | "name":"base",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":true,
10 | "width":9,
11 | "x":0,
12 | "y":0
13 | },
14 | {
15 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 0, 0, 0, 0, 6, 27, 24, 24, 25, 0, 0, 0, 0, 23, 24, 24, 24, 26, 8, 0, 0, 0, 23, 24, 24, 24, 24, 26, 8, 0, 0, 23, 24, 24, 24, 24, 24, 25, 0, 0, 40, 41, 41, 10, 24, 24, 25, 0, 0, 0, 0, 0, 40, 41, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
16 | "height":9,
17 | "name":"grass",
18 | "opacity":1,
19 | "type":"tilelayer",
20 | "visible":true,
21 | "width":9,
22 | "x":0,
23 | "y":0
24 | },
25 | {
26 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | "height":9,
28 | "name":"decoration",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":9,
33 | "x":0,
34 | "y":0
35 | }],
36 | "orientation":"orthogonal",
37 | "properties":
38 | {
39 |
40 | },
41 | "tileheight":64,
42 | "tilesets":[
43 | {
44 | "firstgid":1,
45 | "image":"..\/imgs\/mapPack_tilesheet.png",
46 | "imageheight":768,
47 | "imagewidth":1088,
48 | "margin":0,
49 | "name":"mapPack_tilesheet",
50 | "properties":
51 | {
52 |
53 | },
54 | "spacing":0,
55 | "tileheight":64,
56 | "tilewidth":64
57 | }],
58 | "tilewidth":64,
59 | "version":1,
60 | "width":9
61 | }
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_basic.tmx:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_camera.json:
--------------------------------------------------------------------------------
1 | { "height":10,
2 | "layers":[
3 | {
4 | "data":[203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203],
5 | "height":10,
6 | "name":"base",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":true,
10 | "width":14,
11 | "x":0,
12 | "y":0
13 | },
14 | {
15 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 6, 27, 24, 24, 24, 24, 26, 8, 0, 0, 0, 0, 0, 6, 27, 24, 24, 24, 24, 24, 24, 26, 7, 8, 0, 0, 0, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 0, 0, 0, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 26, 8, 0, 0, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 0, 0, 40, 41, 41, 41, 41, 10, 24, 24, 24, 24, 24, 25, 0, 0, 0, 0, 0, 0, 0, 40, 41, 41, 41, 41, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
16 | "height":10,
17 | "name":"grass",
18 | "opacity":1,
19 | "type":"tilelayer",
20 | "visible":true,
21 | "width":14,
22 | "x":0,
23 | "y":0
24 | },
25 | {
26 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 89, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 104, 73, 89, 70, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 87, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 104, 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | "height":10,
28 | "name":"ice",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":14,
33 | "x":0,
34 | "y":0
35 | },
36 | {
37 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 124, 107, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 124, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
38 | "height":10,
39 | "name":"decoration",
40 | "opacity":1,
41 | "type":"tilelayer",
42 | "visible":true,
43 | "width":14,
44 | "x":0,
45 | "y":0
46 | }],
47 | "orientation":"orthogonal",
48 | "properties":
49 | {
50 | "sx":"0",
51 | "sy":"32"
52 | },
53 | "tileheight":64,
54 | "tilesets":[
55 | {
56 | "firstgid":1,
57 | "image":"..\/imgs\/mapPack_tilesheet.png",
58 | "imageheight":768,
59 | "imagewidth":1088,
60 | "margin":0,
61 | "name":"mapPack_tilesheet",
62 | "properties":
63 | {
64 |
65 | },
66 | "spacing":0,
67 | "tileheight":64,
68 | "tilewidth":64
69 | }],
70 | "tilewidth":64,
71 | "version":1,
72 | "width":14
73 | }
--------------------------------------------------------------------------------
/docs/assets/data/tile_engine_camera.tmx:
--------------------------------------------------------------------------------
1 |
2 |
31 |
--------------------------------------------------------------------------------
/docs/assets/imgs/Attribution.txt:
--------------------------------------------------------------------------------
1 | License
2 | -------
3 |
4 | CC0 1.0 Universal
5 | - http://creativecommons.org/publicdomain/zero/1.0/
6 |
7 | Assets from:
8 | http://opengameart.org/content/platformer-tiles
9 | https://www.kenney.nl/assets/ui-pack
10 |
11 | Kenney.nl
12 | OpenGameArt Kenney
13 | Homepage http://www.kenney.nl/
--------------------------------------------------------------------------------
/docs/assets/imgs/blue_button02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/blue_button02.png
--------------------------------------------------------------------------------
/docs/assets/imgs/blue_button03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/blue_button03.png
--------------------------------------------------------------------------------
/docs/assets/imgs/character.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/character.png
--------------------------------------------------------------------------------
/docs/assets/imgs/character_walk_sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/character_walk_sheet.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/20461-dioretsa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/20461-dioretsa.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/a-day-in-the-life.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/a-day-in-the-life.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/an-offline-life.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/an-offline-life.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/audio-dash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/audio-dash.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-down-the-tower.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-down-the-tower.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-forth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-forth.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-in-dino.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-in-dino.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-on-track.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-on-track.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-to-bathroom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-to-bathroom.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-to-life.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-to-life.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-to-that-platform-i-was-on-before-jumping.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-to-that-platform-i-was-on-before-jumping.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-to-the-island.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-to-the-island.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/back-to-the-nest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/back-to-the-nest.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/backside.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/backside.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/backstabber-hero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/backstabber-hero.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/backstone.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/backstone.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/barry-the-bird.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/barry-the-bird.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/blackout.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/blackout.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/bubble-burst.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/bubble-burst.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/captain-katamov.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/captain-katamov.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/cat-goric-escape-from-the-warp-chamber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/cat-goric-escape-from-the-warp-chamber.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/close-to-me.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/close-to-me.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/detro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/detro.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/dodge-that-shit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/dodge-that-shit.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/earth-that-was.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/earth-that-was.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/eat-my-dust.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/eat-my-dust.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/flight-back-home.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/flight-back-home.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/friendly-alien.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/friendly-alien.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/frog-jumper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/frog-jumper.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/godai-is-back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/godai-is-back.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/grow-back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/grow-back.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/hang-by-a-thread.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/hang-by-a-thread.jpeg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/i-forgot-my-sword.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/i-forgot-my-sword.jpeg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/kontra-game-samples.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/kontra-game-samples.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/kurve-space.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/kurve-space.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/lost-pages.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/lost-pages.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/lost-robot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/lost-robot.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/meadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/meadow.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/mongol-march.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/mongol-march.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/ninja-take-back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/ninja-take-back.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/noegnud.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/noegnud.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/population-404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/population-404.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/send-the-asteroids-back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/send-the-asteroids-back.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/shoot2live.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/shoot2live.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/snek-farm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/snek-farm.png
--------------------------------------------------------------------------------
/docs/assets/imgs/games/start-over.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/start-over.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/tanky-mctankface.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/tanky-mctankface.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/the-waffle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/the-waffle.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/they-follow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/they-follow.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/toe-tac-tic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/toe-tac-tic.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/trench-fisher.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/trench-fisher.jpeg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/troposphere.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/troposphere.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/games/we-must-go-back.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/games/we-must-go-back.jpg
--------------------------------------------------------------------------------
/docs/assets/imgs/mapPack_tilesheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/docs/assets/imgs/mapPack_tilesheet.png
--------------------------------------------------------------------------------
/docs/assets/js/exampleTabList.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let type = localStorage.getItem('kontra-example-type') || 'global';
3 | document.body.setAttribute('data-examples', type);
4 |
5 | Array.from(document.querySelectorAll(`[role=tab]`)).forEach(tab => {
6 | tab.setAttribute('tabindex', -1);
7 | });
8 |
9 | Array.from(document.querySelectorAll(`[data-tab=${type}] [role=tab]`)).forEach(tab => {
10 | tab.setAttribute('aria-selected', true);
11 | tab.setAttribute('tabindex', 0);
12 | });
13 |
14 | Array.from(document.querySelectorAll('[role=tablist]')).forEach(tablist => {
15 | let tabs = [];
16 |
17 | function switchTab(index) {
18 | let tab = tabs[index];
19 | let type = tab.parentElement.dataset.tab;
20 | localStorage.setItem('kontra-example-type', type);
21 | document.body.setAttribute('data-examples', type);
22 |
23 | let priorTab = tablist.querySelector('[aria-selected]');
24 | priorTab.removeAttribute('aria-selected');
25 | priorTab.setAttribute('tabindex', -1);
26 |
27 | tab.setAttribute('aria-selected', true);
28 | tab.focus();
29 | tab.setAttribute('tabindex', 0);
30 | }
31 |
32 | Array.from(tablist.querySelectorAll('[role=tab]')).forEach((tab, index) => {
33 | tabs.push(tab);
34 |
35 | tab.addEventListener('click', e => {
36 | switchTab(index);
37 | });
38 |
39 | tab.addEventListener('keydown', e => {
40 | let tabIndex;
41 | if (e.which === 37) {
42 | tabIndex = index - 1 <= 0 ? 0 : index - 1;
43 | switchTab(tabIndex);
44 | } else if (e.which === 39) {
45 | tabIndex = index + 1 >= tabs.length - 1 ? tabs.length - 1 : index + 1;
46 | switchTab(tabIndex);
47 | }
48 | });
49 | });
50 | });
51 | })();
52 |
--------------------------------------------------------------------------------
/docs/assets/js/navbar.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let nav = document.querySelector('.main-nav');
3 | nav.addEventListener(
4 | 'scroll',
5 | function (e) {
6 | if (this.offsetHeight + this.scrollTop >= this.scrollHeight) {
7 | this.classList.remove('showScroll');
8 | } else {
9 | this.classList.add('showScroll');
10 | }
11 | }.bind(nav)
12 | );
13 |
14 | let menuBtn = document.querySelector('.menu-button');
15 | let menu = document.querySelector('#menu');
16 | menuBtn.addEventListener('click', function () {
17 | let expanded = !(this.getAttribute('aria-expanded') === 'true');
18 | this.setAttribute('aria-expanded', expanded);
19 | document.body.classList.toggle('menu-expanded');
20 | if (expanded) {
21 | menu.style.height = menu.scrollHeight + 'px';
22 | } else {
23 | menu.style.height = null;
24 | }
25 | });
26 |
27 | // save scroll position between page loads
28 | window.addEventListener('unload', function (event) {
29 | sessionStorage.setItem('kontra-nav-scroll', nav.scrollTop);
30 | });
31 | })();
32 |
--------------------------------------------------------------------------------
/docs/assets/js/pool.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let { canvas, context } = kontra.init('pool-example');
3 |
4 | let midX = canvas.width / 2;
5 | let midY = canvas.height / 2;
6 |
7 | let fields = [
8 | {
9 | x: midX - 75,
10 | y: midY + 20,
11 | mass: 900
12 | },
13 | {
14 | x: midX + 25,
15 | y: midY + 10,
16 | mass: -50
17 | }
18 | ];
19 |
20 | let pool = kontra.Pool({
21 | create: kontra.Sprite,
22 | maxSize: 1500,
23 | fill: true
24 | });
25 |
26 | context.font = '18px Arial';
27 |
28 | // redefine sprite update to account for fields
29 | function update() {
30 | // apply field
31 | let totalAccelerationX = 0;
32 | let totalAccelerationY = 0;
33 |
34 | for (let i = 0, field; (field = fields[i]); i++) {
35 | let vectorX = field.x - this.x;
36 | let vectorY = field.y - this.y;
37 |
38 | let force = field.mass / Math.pow(vectorX * vectorX + vectorY * vectorY, 1.5);
39 |
40 | totalAccelerationX += vectorX * force;
41 | totalAccelerationY += vectorY * force;
42 | }
43 |
44 | this.ddx = totalAccelerationX;
45 | this.ddy = totalAccelerationY;
46 |
47 | this.advance();
48 | }
49 |
50 | let loop = kontra.GameLoop({
51 | update: function () {
52 | for (let i = 0; i < 4; i++) {
53 | pool.get({
54 | x: midX + 75,
55 | y: midY,
56 | dx: 2 - Math.random() * 4,
57 | dy: 2 - Math.random() * 4,
58 | color: 'red',
59 | width: 2,
60 | height: 2,
61 | ttl: 300,
62 | update: update
63 | });
64 | }
65 |
66 | pool.update();
67 | },
68 | render: function () {
69 | pool.render();
70 |
71 | context.fillStyle = 'white';
72 | context.fillText(`Object count: ${pool.size}`, 15, 25);
73 | }
74 | });
75 |
76 | loop.start();
77 | })();
78 |
--------------------------------------------------------------------------------
/docs/assets/js/quadtree.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let { canvas, context } = kontra.init('quadtree-example');
3 |
4 | let sprites = [];
5 | for (let i = 0; i < 20; i++) {
6 | sprites.push(
7 | kontra.Sprite({
8 | width: 10,
9 | height: 10,
10 | x: Math.random() * (canvas.width - 10),
11 | y: Math.random() * (canvas.height - 10),
12 | dx: Math.random() * 4 - 2,
13 | dy: Math.random() * 4 - 2,
14 | color: 'red'
15 | })
16 | );
17 | }
18 |
19 | function renderQuadtree(node) {
20 | context.strokeStyle = '#eee';
21 | context.strokeRect(node.bounds.x, node.bounds.y, node.bounds.width, node.bounds.height);
22 |
23 | // render the subnodes so long as this node is a branch and has subnodes
24 | if (node._b && node._s.length) {
25 | for (let i = 0; i < 4; i++) {
26 | renderQuadtree(node._s[i]);
27 | }
28 | }
29 | }
30 |
31 | let quadtree = kontra.Quadtree({
32 | maxObjects: 5
33 | });
34 |
35 | let loop = kontra.GameLoop({
36 | update() {
37 | sprites.forEach(sprite => {
38 | sprite.update();
39 |
40 | if (sprite.x < 0) {
41 | sprite.dx *= -1;
42 | sprite.x = 0;
43 | } else if (sprite.x + sprite.width >= canvas.width) {
44 | sprite.dx *= -1;
45 | sprite.x = canvas.width - sprite.width;
46 | }
47 |
48 | if (sprite.y < 0) {
49 | sprite.dy *= -1;
50 | sprite.y = 0;
51 | } else if (sprite.y + sprite.height >= canvas.height) {
52 | sprite.dy *= -1;
53 | sprite.y = canvas.height - sprite.height;
54 | }
55 | });
56 |
57 | quadtree.clear();
58 | quadtree.add(sprites);
59 | },
60 | render() {
61 | renderQuadtree(quadtree);
62 | sprites.forEach(sprite => sprite.render());
63 | }
64 | });
65 |
66 | loop.start();
67 | })();
68 |
--------------------------------------------------------------------------------
/docs/pages/download.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | Download the latest version of Kontra.
4 |
5 | ## Source Code
6 |
7 | - [Global object production version](https://unpkg.com/kontra@latest/kontra.min.js)
8 | - [Global object development version](https://unpkg.com/kontra@latest/kontra.js)
9 | - [ES Module production version](https://unpkg.com/kontra@latest/kontra.min.mjs)
10 | - [ES Module development version](https://unpkg.com/kontra@latest/kontra.mjs)
11 | - [TGZ file](https://registry.npmjs.org/kontra/-/kontra-__packageVersion__.tgz)
12 | - [Github repo](https://github.com/straker/kontra)
13 |
14 | ## Package managers
15 |
16 | - `npm install kontra`
17 | - `yarn add kontra`
18 |
19 | ## CDN
20 |
21 | ### Global Object
22 |
23 | ```html
24 |
25 | ```
26 | ```html
27 |
28 | ```
29 |
30 | ### ES Module Import
31 |
32 | ```js
33 | // exclude-tablist
34 | import kontra from 'https://cdn.jsdelivr.net/npm/kontra@__packageVersion__/kontra.min.mjs';
35 | ```
36 | ```js
37 | // exclude-tablist
38 | import kontra from 'https://cdn.jsdelivr.net/npm/kontra@__packageVersion__/kontra.mjs';
39 | ```
40 |
41 | @section Download
42 | @page download
43 | @packageVersion
44 |
45 | */
46 |
--------------------------------------------------------------------------------
/docs/pages/index.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | Kontra is a lightweight JavaScript gaming micro-library created specifically for the [Js13kGames](http://js13kgames.com/) game jam.
4 |
5 |
6 |
7 |
8 | The goal of Kontra is not to implement everything you could possibly need to make a game. There are already libraries out there that do that (like [Phaser](http://phaser.io/)).
9 |
10 | Instead, Kontra aims to implement basic game requirements like asset loading, input, the game loop, and sprites to keep the library very small and focused. This allows it to be used when your game size is limited (a la Js13k).
11 |
12 | Kontra does provide some more advanced data structures like object pools and quadtrees that have been fine tuned to be small, fast, and memory efficient.
13 |
14 | Kontra prides itself in being:
15 |
16 | - **Lightweight**: tries to take up as little room as possible and strives to reduce the file size every major release.
17 | - **Modular**: pick and choose what you want through ES modules.
18 | - **Extensible**: everything is customizable and can be extended.
19 | - **Fast**: all logic has been removed from the update and render cycles.
20 | - **Memory conscious**: takes up as little memory as needed and tries not to be wasteful about the memory it does take up.
21 |
22 | ## Use it When
23 |
24 | - You want to get something up and running fairly quickly.
25 | - You want a basic structure that is easy to scale and extend.
26 | - In conjunction with other libraries (like [Playground.js](https://github.com/rezoner/playground)).
27 | - Prototyping.
28 | - Game jams.
29 |
30 | ## Ready to Get Going?
31 |
32 | - Read the [getting started guide](getting-started) and some [tutorials](tutorials).
33 | - Look at [games made with Kontra](made-with-kontra).
34 | - Gain in-depth knowledge from the API docs.
35 |
36 | ## Support Future Development
37 |
38 | Kontra.js is made possible by users like you. Through helping find issues, opening pull requests, and [funding continuous development](https://www.patreon.com/straker), Kontra.js can continue to provide you with quality improvements and updates.
39 |
40 | When you become a Patron, you get access to behind the scenes development logs, the ability to vote on which features to work on next, and early access to development builds.
41 |
42 | ### Top Patrons
43 |
44 | - Karar Al-Remahy
45 | - UnbrandedTech
46 | - Innkeeper Games
47 |
48 | @section What is Kontra.js?
49 | @page index
50 |
51 | */
52 |
--------------------------------------------------------------------------------
/docs/pages/tutorials.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | A list of articles and tutorials for making games with Kontra.js. I've collected as many as I could all in one place for your reading pleasure.
4 |
5 | Have a tutorial you want to add? [Let me know](https://github.com/straker/kontra/issues)!
6 |
7 | - [Js13kGames Video Tutorial Series](https://gamedevacademy.org/js13kgames-tutorial-video-series/) – Zenva
8 | - [Making Asteroids with Kontra.js and Web Maker](https://medium.com/web-maker/making-asteroids-with-kontra-js-and-web-maker-95559d39b45f) – Steven Lambert
9 | - [How to Write a Game in Under 13 Kb While Taking Care of a Baby](https://www.barbarianmeetscoding.com/blog/2018/09/19/how-to-write-a-game-under-13k-while-taking-care-of-a-baby/) – Jaime González García
10 |
11 | @section Tutorials
12 | @page tutorials
13 |
14 | */
15 |
--------------------------------------------------------------------------------
/docs/service-worker.js:
--------------------------------------------------------------------------------
1 | let prefix = '/kontra';
2 |
3 | // adjust path based on location (github pages required kontra url)
4 | if (['localhost', '127.0.0.1'].includes(self.location.hostname)) {
5 | prefix = '';
6 | }
7 |
8 | const filesToCache = [
9 | // pages
10 | `${prefix}/`,
11 |
12 | // js
13 | `${prefix}/assets/js/kontra.js`,
14 | `${prefix}/assets/js/navbar.js`,
15 | `${prefix}/assets/js/exampleTabList.js`,
16 |
17 | // styles
18 | `${prefix}/assets/styles.css`
19 | ];
20 |
21 | const staticCacheName = 'kontra-docs-v10.0.0';
22 |
23 | // cache the application shell
24 | self.addEventListener('install', event => {
25 | event.waitUntil(
26 | caches.open(staticCacheName).then(cache => {
27 | return cache.addAll(filesToCache);
28 | })
29 | );
30 | });
31 |
32 | // serve files from the cache
33 | self.addEventListener('fetch', event => {
34 | // respond with the cache first
35 | event.respondWith(
36 | caches
37 | .match(event.request)
38 | .then(response => {
39 | if (response) {
40 | return response;
41 | }
42 |
43 | // progressively add new files to the cache
44 | return updateCache(event.request);
45 | })
46 | .catch(error => {
47 | // TODO 6 - Respond with custom offline page
48 | })
49 | );
50 |
51 | // update the cache with newest version
52 | event.waitUntil(updateCache(event.request));
53 | });
54 |
55 | function updateCache(request) {
56 | return fetch(request).then(response => {
57 | return caches.open(staticCacheName).then(cache => {
58 | cache.put(request.url, response.clone());
59 | return response;
60 | });
61 | });
62 | }
63 |
64 | // delete outdated caches
65 | self.addEventListener('activate', event => {
66 | const cacheAllowlist = [staticCacheName];
67 |
68 | event.waitUntil(
69 | caches.keys().then(cacheNames => {
70 | return Promise.all(
71 | cacheNames.map(cacheName => {
72 | if (cacheAllowlist.indexOf(cacheName) === -1) {
73 | return caches.delete(cacheName);
74 | }
75 | })
76 | );
77 | })
78 | );
79 | });
80 |
--------------------------------------------------------------------------------
/docs/template/partials/example-output.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 |
15 |
16 |
17 | {{ example.globalOutput }}
18 |
19 |
20 | {{ example.esOutput }}
21 |
22 |
23 | {{ example.bundleOutput }}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/template/partials/parameter-table.hbs:
--------------------------------------------------------------------------------
1 | {{#if param}}
2 | {{ name }} Parameters
3 |
4 | {{#each param}}
5 | -
6 |
{{ name }}
7 | {{#if optional}}Optional{{/if}}
8 |
9 | - {{{ description }}}
10 | {{/each}}
11 |
12 | {{/if}}
13 |
14 | {{#if returns}}
15 | {{ name }} Return value
16 | {{{ returns.description }}}
17 | {{/if}}
--------------------------------------------------------------------------------
/examples/audio/Attribution.txt:
--------------------------------------------------------------------------------
1 | License
2 | -------
3 |
4 | CC BY 3.0
5 | - https://creativecommons.org/licenses/by/3.0/
6 |
7 | Assets from:
8 | https://opengameart.org/content/digital-forest
9 |
10 | Pyerye
11 | OpenGameArt Pyerye
12 | Homepage https://opengameart.org/users/alundra
--------------------------------------------------------------------------------
/examples/audio/Digital_Forest.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/audio/Digital_Forest.mp3
--------------------------------------------------------------------------------
/examples/audio/Digital_Forest.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/audio/Digital_Forest.ogg
--------------------------------------------------------------------------------
/examples/button/button.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Button
5 |
6 |
7 |
8 |
9 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/examples/button/buttonImage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Button Image
5 |
6 |
7 |
8 |
9 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/examples/button/buttonStates.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Button States
5 |
6 |
7 |
8 |
9 |
10 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/examples/galaxian/imgs/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/imgs/bg.png
--------------------------------------------------------------------------------
/examples/galaxian/imgs/bullet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/imgs/bullet.png
--------------------------------------------------------------------------------
/examples/galaxian/imgs/bullet_enemy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/imgs/bullet_enemy.png
--------------------------------------------------------------------------------
/examples/galaxian/imgs/enemy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/imgs/enemy.png
--------------------------------------------------------------------------------
/examples/galaxian/imgs/ship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/imgs/ship.png
--------------------------------------------------------------------------------
/examples/galaxian/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Kontra Test
5 |
78 |
79 |
80 |
81 |
82 |
83 | M - Mute
84 | P - Pause
85 |
86 |
90 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/examples/galaxian/js/boot.js:
--------------------------------------------------------------------------------
1 | let assets = [
2 | // images
3 | 'ship.png',
4 | 'bg.png',
5 | 'bullet_enemy.png',
6 | 'bullet.png',
7 | 'enemy.png',
8 |
9 | // audios
10 | 'explosion.mp3',
11 | 'game_over.mp3',
12 | 'kick_shock.mp3',
13 | 'laser.mp3'
14 | ];
15 |
16 | let canvas = kontra.getCanvas();
17 | let context = kontra.getContext();
18 |
19 | let loadingText = kontra.Text({
20 | color: '#FF7F00',
21 | text: 'Loading...',
22 | font: '18px Helvetica, sans-serif',
23 | anchor: { x: 0.5, y: 0.5 }
24 | });
25 |
26 | // create a progress bar
27 | let loadingBar = kontra.Sprite({
28 | width: canvas.width / 2,
29 | height: 30,
30 | progress: 0,
31 | anchor: { x: 0.5, y: 0.5 },
32 | render() {
33 | context.strokeStyle = 'white';
34 | context.strokeRect(0, 0, this.width, this.height);
35 |
36 | context.fillStyle = 'green';
37 | context.fillRect(1, 1, this.width * (this.progress / assets.length) - 2, this.height - 2);
38 | }
39 | });
40 |
41 | let playButton = kontra.Button({
42 | padX: 15,
43 | padY: 15,
44 | anchor: { x: 0.5, y: 0.5 },
45 |
46 | // align center to the grid
47 | justifySelf: 'center',
48 |
49 | text: {
50 | text: 'Play',
51 | color: '#FF7F00',
52 | font: '28px Helvetica, sans-serif',
53 | anchor: { x: 0.5, y: 0.5 }
54 | },
55 |
56 | render() {
57 | if (this.disabled) {
58 | this.textNode.color = '#999';
59 | return;
60 | }
61 |
62 | context.strokeStyle = 'white';
63 | context.strokeRect(1, 1, this.width - 2, this.height - 2);
64 |
65 | if (this.pressed) {
66 | this.textNode.color = 'white';
67 | } else if (this.focused || this.hovered) {
68 | this.textNode.color = '#62a2f9';
69 | canvas.style.cursor = 'pointer';
70 | } else {
71 | this.textNode.color = '#FF7F00';
72 | canvas.style.cursor = 'initial';
73 | }
74 | },
75 |
76 | onUp() {
77 | kontra.emit('startGame');
78 | }
79 | });
80 | playButton.disable();
81 |
82 | // let the grid manager handle auto placing the controls
83 | let grid = kontra.Grid({
84 | x: canvas.width / 2,
85 | y: canvas.height / 2,
86 |
87 | // put extra space between the button and progress bar
88 | gapY: [0, 40],
89 | anchor: { x: 0.5, y: 0.5 },
90 | children: [loadingText, loadingBar, playButton]
91 | });
92 |
93 | let bootScene = kontra.Scene({
94 | id: 'boot',
95 | objects: [grid]
96 | });
97 |
98 | // set default asset paths
99 | kontra.setImagePath('imgs/');
100 | kontra.setAudioPath('sounds/');
101 |
102 | // asset progress
103 | kontra.on('assetLoaded', (asset, url) => {
104 | loadingBar.progress++;
105 | });
106 |
107 | // load assets
108 | kontra.load(...assets).then(() => {
109 | kontra.emit('doneLoading');
110 | playButton.enable();
111 | playButton.focus();
112 | });
113 |
114 | export default bootScene;
115 |
--------------------------------------------------------------------------------
/examples/galaxian/js/gameOver.js:
--------------------------------------------------------------------------------
1 | let canvas = kontra.getCanvas();
2 | let context = kontra.getContext();
3 |
4 | let gameOverText = kontra.Text({
5 | color: '#FF7F00',
6 | text: 'Game Over',
7 | font: '28px Helvetica, sans-serif',
8 | anchor: { x: 0.5, y: 0.5 }
9 | });
10 |
11 | let restartButton = kontra.Button({
12 | padX: 15,
13 | padY: 15,
14 | anchor: { x: 0.5, y: 0.5 },
15 |
16 | // align center to the grid
17 | justifySelf: 'center',
18 |
19 | text: {
20 | text: 'Restart',
21 | color: '#FF7F00',
22 | font: '18px Helvetica, sans-serif',
23 | anchor: { x: 0.5, y: 0.5 }
24 | },
25 |
26 | render() {
27 | if (this.disabled) {
28 | this.textNode.color = '#999';
29 | return;
30 | }
31 |
32 | context.strokeStyle = 'white';
33 | context.strokeRect(1, 1, this.width - 2, this.height - 2);
34 |
35 | if (this.pressed) {
36 | this.textNode.color = 'white';
37 | } else if (this.focused || this.hovered) {
38 | this.textNode.color = '#62a2f9';
39 | canvas.style.cursor = 'pointer';
40 | } else {
41 | this.textNode.color = '#FF7F00';
42 | canvas.style.cursor = 'initial';
43 | }
44 | },
45 |
46 | onUp() {
47 | kontra.emit('startGame');
48 | }
49 | });
50 |
51 | // let the grid manager handle auto placing the controls
52 | let grid = kontra.Grid({
53 | x: canvas.width / 2,
54 | y: canvas.height / 2,
55 |
56 | // put extra space between the button and text
57 | gapY: 40,
58 | anchor: { x: 0.5, y: 0.5 },
59 | children: [gameOverText, restartButton]
60 | });
61 |
62 | let gameOverScene = kontra.Scene({
63 | id: 'gameOver',
64 | objects: [grid]
65 | });
66 | export default gameOverScene;
67 |
--------------------------------------------------------------------------------
/examples/galaxian/js/index.js:
--------------------------------------------------------------------------------
1 | import bootScene from './boot.js';
2 | import getGameScene from './galaxian.js';
3 | import gameOverScene from './gameOver.js';
4 |
5 | let gameScene;
6 | let activeScene = bootScene;
7 | let isGameOver = false;
8 |
9 | let loop = kontra.GameLoop({
10 | update() {
11 | if (isGameOver) {
12 | gameOverScene.update();
13 | } else {
14 | activeScene.update();
15 | }
16 | },
17 | render() {
18 | activeScene.render();
19 |
20 | if (isGameOver) {
21 | gameOverScene.render();
22 | }
23 | }
24 | });
25 |
26 | // pause/unpause game
27 | kontra.onKey('p', function () {
28 | if (loop.isStopped) {
29 | loop.start();
30 | kontra.audioAssets.kick_shock.play();
31 | } else {
32 | loop.stop();
33 | kontra.audioAssets.kick_shock.pause();
34 | }
35 | });
36 |
37 | kontra.on('startGame', () => {
38 | isGameOver = false;
39 |
40 | if (!gameScene) {
41 | gameScene = getGameScene();
42 | }
43 |
44 | bootScene.destroy();
45 | activeScene = gameScene;
46 | gameScene.start();
47 | });
48 |
49 | kontra.on('gameOver', () => {
50 | isGameOver = true;
51 |
52 | kontra.audioAssets.kick_shock.pause();
53 | kontra.audioAssets.game_over.currentTime = 0;
54 | kontra.audioAssets.game_over.play();
55 | });
56 |
57 | loop.start();
58 |
--------------------------------------------------------------------------------
/examples/galaxian/sounds/explosion.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/sounds/explosion.mp3
--------------------------------------------------------------------------------
/examples/galaxian/sounds/game_over.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/sounds/game_over.mp3
--------------------------------------------------------------------------------
/examples/galaxian/sounds/kick_shock.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/sounds/kick_shock.mp3
--------------------------------------------------------------------------------
/examples/galaxian/sounds/laser.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/galaxian/sounds/laser.mp3
--------------------------------------------------------------------------------
/examples/gamepad/gamepad.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Gamepad
5 |
6 |
7 |
8 | NOTE:Gamepad support requires using a secure context (HTTPS).
9 |
10 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/examples/gesture/gesture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Gesture
5 |
6 |
7 |
8 |
9 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/examples/imgs/Attribution.txt:
--------------------------------------------------------------------------------
1 | License
2 | -------
3 |
4 | CC0 1.0 Universal
5 | - http://creativecommons.org/publicdomain/zero/1.0/
6 |
7 | Assets from:
8 | http://opengameart.org/content/platformer-tiles
9 | https://opengameart.org/content/classic-hero
10 | https://www.kenney.nl/assets/ui-pack
11 | https://www.kenney.nl/assets/topdown-tanks-redux
12 |
13 | Kenny.nl
14 | OpenGameArt Kenny
15 | Homepage http://www.kenney.nl/
--------------------------------------------------------------------------------
/examples/imgs/blue_button02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/blue_button02.png
--------------------------------------------------------------------------------
/examples/imgs/bulletBlue1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/bulletBlue1.png
--------------------------------------------------------------------------------
/examples/imgs/bulletGreen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/bulletGreen1.png
--------------------------------------------------------------------------------
/examples/imgs/bulletRed1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/bulletRed1.png
--------------------------------------------------------------------------------
/examples/imgs/bulletSand1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/bulletSand1.png
--------------------------------------------------------------------------------
/examples/imgs/character.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/character.png
--------------------------------------------------------------------------------
/examples/imgs/character_walk_sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/character_walk_sheet.png
--------------------------------------------------------------------------------
/examples/imgs/oldHero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/oldHero.png
--------------------------------------------------------------------------------
/examples/imgs/tank_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/tank_blue.png
--------------------------------------------------------------------------------
/examples/imgs/tank_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/tank_green.png
--------------------------------------------------------------------------------
/examples/imgs/tank_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/tank_red.png
--------------------------------------------------------------------------------
/examples/imgs/tank_sand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/imgs/tank_sand.png
--------------------------------------------------------------------------------
/examples/pointer/pointer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pointer
5 |
6 |
7 |
8 |
9 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/examples/pointer/touchEvents.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Pointer
7 |
8 |
9 |
10 |
11 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/examples/pool/particleEngine.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Particle Engine
5 |
6 |
7 |
8 |
9 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/examples/prism/codeOutput.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var pre = document.createElement('pre');
3 | pre.innerHTML =
4 | '' +
5 | document.getElementById('code').innerHTML +
6 | '
';
7 | document.body.appendChild(pre);
8 |
9 | var prisimcss = document.createElement('link');
10 | prisimcss.setAttribute('rel', 'stylesheet');
11 | prisimcss.href = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism.min.css';
12 | document.head.appendChild(prisimcss);
13 |
14 | var prisimjs = document.createElement('script');
15 | prisimjs.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.min.js';
16 | document.body.appendChild(prisimjs);
17 | })();
18 |
--------------------------------------------------------------------------------
/examples/scene/depthSortObjects.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rect Sprite
5 |
6 |
7 |
8 |
9 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/scene/gameScene.js:
--------------------------------------------------------------------------------
1 | import { gridSize, width, height } from './globals.js';
2 | import snake from './snake.js';
3 |
4 | let apple = kontra.Sprite({
5 | x: 320,
6 | y: 320,
7 | width: gridSize - 1,
8 | height: gridSize - 1,
9 | color: 'red'
10 | });
11 |
12 | // keep track of where snake is so we know where we can place a new apple
13 | let availableCells = [];
14 |
15 | let gameScene = kontra.Scene({
16 | id: 'game',
17 | update() {
18 | snake.update();
19 |
20 | // snake ate apple
21 | if (snake.x === apple.x && snake.y === apple.y) {
22 | snake.maxCells++;
23 |
24 | availableCells = [];
25 | for (let row = 0; row < height - 1; row++) {
26 | for (let col = 0; col < width - 1; col++) {
27 | let snakeCell = snake.cells.find(cell => {
28 | return cell.x === col * gridSize && cell.y === row * gridSize;
29 | });
30 | if (!snakeCell) {
31 | availableCells.push({ row, col });
32 | }
33 | }
34 | }
35 |
36 | let randomCell = kontra.randInt(0, availableCells.length - 1);
37 | apple.x = availableCells[randomCell].col * gridSize;
38 | apple.y = availableCells[randomCell].row * gridSize;
39 | }
40 |
41 | // check collision with all cells after this one
42 | snake.cells.forEach((cell, index) => {
43 | for (var i = index + 1; i < snake.cells.length; i++) {
44 | // snake occupies same space as a body part. reset game
45 | if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
46 | snake.x = 160;
47 | snake.y = 160;
48 | snake.cells = [];
49 | snake.maxCells = 4;
50 | snake.dx = gridSize;
51 | snake.dy = 0;
52 |
53 | apple.x = kontra.randInt(0, width) * gridSize;
54 | apple.y = kontra.randInt(0, height) * gridSize;
55 | }
56 | }
57 | });
58 | }
59 | });
60 |
61 | gameScene.add(snake, apple);
62 |
63 | kontra.onKey('esc', () => {
64 | kontra.emit('navigate', 'Menu');
65 | });
66 |
67 | export default gameScene;
68 |
--------------------------------------------------------------------------------
/examples/scene/globals.js:
--------------------------------------------------------------------------------
1 | export let gridSize = 32;
2 | export let width = kontra.getCanvas().width / gridSize;
3 | export let height = kontra.getCanvas().height / gridSize;
--------------------------------------------------------------------------------
/examples/scene/menuScene.js:
--------------------------------------------------------------------------------
1 | let canvas = kontra.getCanvas();
2 |
3 | let startButton = kontra.Button({
4 | text: {
5 | color: 'white',
6 | font: '30px Monospace',
7 | text: 'Start',
8 | anchor: { x: 0.5, y: 0.5 }
9 | },
10 | anchor: { x: 0.5, y: 0.5 },
11 | x: canvas.width / 2,
12 | y: canvas.height / 2,
13 | onUp() {
14 | kontra.emit('navigate', this.text);
15 | },
16 | render() {
17 | this.draw();
18 |
19 | if (this.focused || this.hovered) {
20 | this.textNode.color = 'red';
21 | } else {
22 | this.textNode.color = 'white';
23 | }
24 | }
25 | });
26 |
27 | kontra.track(startButton);
28 |
29 | let menuScene = kontra.Scene({
30 | id: 'menu',
31 | onShow() {
32 | startButton.text = 'Resume';
33 | startButton.focus();
34 | },
35 | focus() {
36 | startButton.focus();
37 | }
38 | });
39 |
40 | menuScene.add(startButton);
41 |
42 | export default menuScene;
43 |
--------------------------------------------------------------------------------
/examples/scene/simpleScenes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Scene
5 |
6 |
24 |
25 |
26 |
27 |
28 |
(Esc) Menu
29 |
30 |
35 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/scene/snake.js:
--------------------------------------------------------------------------------
1 | import { gridSize } from './globals.js';
2 |
3 | let snake = kontra.Sprite({
4 | x: 160,
5 | y: 160,
6 | width: gridSize,
7 | height: gridSize,
8 |
9 | // snake velocity. moves one grid length every frame in either the x or y direction
10 | dx: gridSize,
11 | dy: 0,
12 |
13 | // keep track of all grids the snake body occupies
14 | cells: [],
15 |
16 | // keep track of moves
17 | queue: [],
18 |
19 | // length of the snake. grows when eating an apple
20 | maxCells: 4,
21 |
22 | update() {
23 | let move = this.queue.shift();
24 | if (move) {
25 | this.dx = move.dx;
26 | this.dy = move.dy;
27 | }
28 |
29 | this.advance();
30 | let canvas = kontra.getCanvas();
31 |
32 | // wrap snake position horizontally on edge of screen
33 | if (this.x < 0) {
34 | this.x = canvas.width - gridSize;
35 | } else if (this.x >= canvas.width) {
36 | this.x = 0;
37 | }
38 |
39 | // wrap snake position vertically on edge of screen
40 | if (this.y < 0) {
41 | this.y = canvas.height - gridSize;
42 | } else if (this.y >= canvas.height) {
43 | this.y = 0;
44 | }
45 |
46 | // keep track of where snake has been. front of the array is always the head
47 | this.cells.unshift({ x: this.x, y: this.y });
48 |
49 | // remove cells as we move away from them
50 | if (this.cells.length > this.maxCells) {
51 | this.cells.pop();
52 | }
53 | },
54 |
55 | render() {
56 | this.context.fillStyle = 'green';
57 | snake.cells.forEach((cell, index) => {
58 | // drawing 1 px smaller than the grid creates a grid effect in the snake body so you can see how long it is
59 | this.context.fillRect(cell.x - this.x, cell.y - this.y, gridSize - 1, gridSize - 1);
60 | });
61 | }
62 | });
63 |
64 | // prevent snake from backtracking on itself by checking that it's
65 | // not already moving on the same axis (pressing left while moving
66 | // left won't do anything, and pressing right while moving left
67 | // shouldn't let you collide with your own body)
68 | kontra.onKey('arrowleft', () => {
69 | if (snake.dx === 0) {
70 | // queue the move so we also don't change directions before an
71 | // update and still can run into ourselves
72 | snake.queue.push({
73 | dx: -gridSize,
74 | dy: 0
75 | });
76 | }
77 | });
78 | kontra.onKey('arrowup', () => {
79 | if (snake.dy === 0) {
80 | snake.queue.push({
81 | dy: -gridSize,
82 | dx: 0
83 | });
84 | }
85 | });
86 | kontra.onKey('arrowright', () => {
87 | snake.queue.push({
88 | dx: gridSize,
89 | dy: 0
90 | });
91 | });
92 | kontra.onKey('arrowdown', () => {
93 | if (snake.dy === 0) {
94 | snake.queue.push({
95 | dy: gridSize,
96 | dx: 0
97 | });
98 | }
99 | });
100 |
101 | export default snake;
102 |
--------------------------------------------------------------------------------
/examples/sprite/animationSprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Animation Sprite
5 |
6 |
7 |
8 |
9 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/examples/sprite/clampSpriteMovement.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Controlling A Sprite
5 |
6 |
7 |
8 |
9 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/examples/sprite/controllingASprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Controlling A Sprite
5 |
6 |
7 |
8 |
9 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/examples/sprite/customUpdateAndDraw.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Controlling A Sprite
5 |
6 |
7 |
8 |
9 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/examples/sprite/extendingASprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sprite Collision
5 |
6 |
7 |
8 |
9 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/examples/sprite/imageSprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Image Sprite
5 |
6 |
7 |
8 |
9 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/examples/sprite/movingASprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Moving A Sprite
5 |
6 |
7 |
8 |
9 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/sprite/rectSprite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rect Sprite
5 |
6 |
7 |
8 |
9 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/sprite/spriteCollision.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sprite Collision
5 |
6 |
7 |
8 |
9 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/spriteSheet/margin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Animation Sprite
5 |
6 |
7 |
8 |
9 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/examples/text/text.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Text
5 |
6 |
7 |
8 |
9 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/text/textAlign.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Text Align
5 |
6 |
7 |
8 |
9 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/text/textAutoNewline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Text Auto Newline
5 |
6 |
7 |
8 |
9 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/text/textLineHeight.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Text LineHeight
5 |
6 |
7 |
8 |
9 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/text/textNewline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Text Newline
5 |
6 |
7 |
8 |
9 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/tileEngine/camera/Attribution.txt:
--------------------------------------------------------------------------------
1 | License
2 | -------
3 |
4 | CC-BY-SA 3.0:
5 | - http://creativecommons.org/licenses/by-sa/3.0/
6 | - See the file: cc-by-sa-3.0.txt
7 | GNU GPL 3.0:
8 | - http://www.gnu.org/licenses/gpl-3.0.html
9 | - See the file: gpl-3.0.txt
10 |
11 | Note the file is based on the LCP contest readme so don't expect the exact little pieces used like the base one.
12 | *Additional license information.
13 |
14 | Assets from:
15 |
16 | LPC participants:
17 | ----------------
18 |
19 | Casper Nilsson
20 | *GNU GPL 3.0 or later
21 | email: casper.nilsson@gmail.com
22 | Freenode: CasperN
23 | OpenGameArt.org: C.Nilsson
24 |
25 | - LPC C.Nilsson (2D art)
26 |
27 | Daniel Eddeland
28 | *GNU GPL 3.0 or later
29 | - Tilesets of plants, props, food and environments, suitable for farming / fishing sims and other games.
30 | - Includes wheat, grass, sand tilesets, fence tilesets and plants such as corn and tomato.
31 |
32 |
33 | Johann CHARLOT
34 | *GNU LGPL Version 3.
35 | *Later versions are permitted.
36 | Homepage http://poufpoufproduction.fr
37 | Email johannc@poufpoufproduction.fr
38 |
39 | - Shoot'em up graphic kit
40 |
41 | Skyler Robert Colladay
42 |
43 | - FeralFantom's Entry (2D art)
44 |
45 | BASE assets:
46 | ------------
47 |
48 | Lanea Zimmerman (AKA Sharm)
49 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
50 |
51 | - barrel.png
52 | - brackish.png
53 | - buckets.png
54 | - bridges.png
55 | - cabinets.png
56 | - cement.png
57 | - cementstair.png
58 | - chests.png
59 | - country.png
60 | - cup.png
61 | - dirt2.png
62 | - dirt.png
63 | - dungeon.png
64 | - grassalt.png
65 | - grass.png
66 | - holek.png
67 | - holemid.png
68 | - hole.png
69 | - house.png
70 | - inside.png
71 | - kitchen.png
72 | - lava.png
73 | - lavarock.png
74 | - mountains.png
75 | - rock.png
76 | - shadow.png
77 | - signs.png
78 | - stairs.png
79 | - treetop.png
80 | - trunk.png
81 | - waterfall.png
82 | - watergrass.png
83 | - water.png
84 | - princess.png and princess.xcf
85 |
86 |
87 | Stephen Challener (AKA Redshrike)
88 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89 |
90 | - female_walkcycle.png
91 | - female_hurt.png
92 | - female_slash.png
93 | - female_spellcast.png
94 | - male_walkcycle.png
95 | - male_hurt.png
96 | - male_slash.png
97 | - male_spellcast.png
98 | - male_pants.png
99 | - male_hurt_pants.png
100 | - male_fall_down_pants.png
101 | - male_slash_pants.png
102 |
103 |
104 | Charles Sanchez (AKA CharlesGabriel)
105 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106 |
107 | - bat.png
108 | - bee.png
109 | - big_worm.png
110 | - eyeball.png
111 | - ghost.png
112 | - man_eater_flower.png
113 | - pumpking.png
114 | - slime.png
115 | - small_worm.png
116 | - snake.png
117 |
118 |
119 | Manuel Riecke (AKA MrBeast)
120 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
121 |
122 | - hairfemale.png and hairfemale.xcf
123 | - hairmale.png and hairmale.xcf
124 | - soldier.png
125 | - soldier_altcolor.png
126 |
127 |
128 | Daniel Armstrong (AKA HughSpectrum)
129 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130 |
131 | Castle work:
132 |
133 | - castlewalls.png
134 | - castlefloors.png
135 | - castle_outside.png
136 | - castlefloors_outside.png
137 | - castle_lightsources.png
138 |
139 |
140 |
--------------------------------------------------------------------------------
/examples/tileEngine/camera/chicken_eat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/tileEngine/camera/chicken_eat.png
--------------------------------------------------------------------------------
/examples/tileEngine/camera/man.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/tileEngine/camera/man.png
--------------------------------------------------------------------------------
/examples/tileEngine/camera/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/tileEngine/camera/terrain.png
--------------------------------------------------------------------------------
/examples/tileEngine/margin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Tile Engine
5 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/examples/tileEngine/margin/roguelikeDungeon_transparent.json:
--------------------------------------------------------------------------------
1 | { "columns":28,
2 | "image":"./roguelikeDungeon_transparent.png",
3 | "imageheight":305,
4 | "imagewidth":492,
5 | "margin":1,
6 | "name":"roguelikeDungeon_transparent",
7 | "spacing":1,
8 | "tilecount":476,
9 | "tileheight":16,
10 | "tilewidth":16,
11 | "type":"tileset"
12 | }
--------------------------------------------------------------------------------
/examples/tileEngine/margin/roguelikeDungeon_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/examples/tileEngine/margin/roguelikeDungeon_transparent.png
--------------------------------------------------------------------------------
/examples/tileEngine/margin/roguelikeDungeon_transparent.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const rename = require('gulp-rename');
3 | const size = require('gulp-size');
4 | const terser = require('gulp-terser');
5 | const plumber = require('gulp-plumber');
6 | const preprocess = require('gulp-preprocess');
7 | const rollup = require('@rollup/stream');
8 | const source = require('vinyl-source-stream');
9 | const pkg = require('./package.json');
10 | require('./tasks/docs.js');
11 | require('./tasks/typescript.js');
12 |
13 | const context = {
14 | GAMEOBJECT_GROUP: true,
15 | GAMEOBJECT_ROTATION: true,
16 | GAMEOBJECT_VELOCITY: true,
17 | GAMEOBJECT_ACCELERATION: true,
18 | GAMEOBJECT_TTL: true,
19 | GAMEOBJECT_ANCHOR: true,
20 | GAMEOBJECT_SCALE: true,
21 | GAMEOBJECT_OPACITY: true,
22 | SPRITE_IMAGE: true,
23 | SPRITE_ANIMATION: true,
24 | TEXT_AUTONEWLINE: true,
25 | TEXT_NEWLINE: true,
26 | TEXT_RTL: true,
27 | TEXT_ALIGN: true,
28 | TEXT_STROKE: true,
29 | TILEENGINE_CAMERA: true,
30 | TILEENGINE_DYNAMIC: true,
31 | TILEENGINE_QUERY: true,
32 | TILEENGINE_TILED: true,
33 | VECTOR_SUBTRACT: true,
34 | VECTOR_SCALE: true,
35 | VECTOR_NORMALIZE: true,
36 | VECTOR_DOT: true,
37 | VECTOR_LENGTH: true,
38 | VECTOR_DISTANCE: true,
39 | VECTOR_ANGLE: true,
40 | VECTOR_DIRECTION: true,
41 | VECTOR_CLAMP: true
42 | // DEBUG and VISUAL_DEBUG are turned off
43 | };
44 |
45 | const headerComment = `/**
46 | * @preserve
47 | * Kontra.js v${pkg.version}
48 | */`;
49 |
50 | function buildIife() {
51 | return rollup({
52 | input: './src/kontra.defaults.js',
53 | output: {
54 | format: 'iife',
55 | name: 'kontra',
56 | strict: false,
57 | banner: headerComment
58 | }
59 | })
60 | .pipe(source('kontra.js'))
61 | .pipe(gulp.dest('.'))
62 | .pipe(gulp.dest('./docs/assets/js'));
63 | }
64 |
65 | function buildModule() {
66 | return rollup({
67 | input: './src/kontra.js',
68 | output: {
69 | format: 'es',
70 | strict: false,
71 | banner: headerComment
72 | }
73 | })
74 | .pipe(source('kontra.mjs'))
75 | .pipe(gulp.dest('.'));
76 | }
77 |
78 | function distIife() {
79 | return gulp
80 | .src('kontra.js')
81 | .pipe(preprocess({ context }))
82 | .pipe(plumber())
83 | .pipe(terser())
84 | .pipe(plumber.stop())
85 | .pipe(gulp.dest('./docs/assets/js'))
86 | .pipe(rename('kontra.min.js'))
87 | .pipe(
88 | size({
89 | showFiles: true
90 | })
91 | )
92 | .pipe(
93 | size({
94 | showFiles: true,
95 | gzip: true
96 | })
97 | )
98 | .pipe(gulp.dest('.'));
99 | }
100 |
101 | function distModule() {
102 | return gulp
103 | .src('kontra.mjs')
104 | .pipe(preprocess({ context }))
105 | .pipe(plumber())
106 | .pipe(terser())
107 | .pipe(plumber.stop())
108 | .pipe(rename('kontra.min.mjs'))
109 | .pipe(
110 | size({
111 | showFiles: true
112 | })
113 | )
114 | .pipe(
115 | size({
116 | showFiles: true,
117 | gzip: true
118 | })
119 | )
120 | .pipe(gulp.dest('.'));
121 | }
122 |
123 | gulp.task('build', gulp.series(buildIife, buildModule, 'build:docs', 'build:ts'));
124 |
125 | gulp.task('dist', gulp.series('build', distIife, distModule));
126 |
127 | gulp.task('watch', function () {
128 | gulp.watch('src/*.js', gulp.series('build', 'dist'));
129 | });
130 |
131 | gulp.task('default', gulp.series('build', 'watch'));
132 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Tue Apr 07 2015 23:14:35 GMT-0600 (MDT)
3 | const DEBUG = process.argv.find(argv => argv === '--debug');
4 |
5 | module.exports = function (config) {
6 | config.set({
7 | basePath: '',
8 | singleRun: false,
9 | autoWatch: true,
10 | frameworks: ['mocha', 'chai', 'sinon'],
11 | files: [
12 | // setup
13 | { pattern: 'test/setup.js', type: 'module' },
14 |
15 | // assets
16 | { pattern: 'test/imgs/**/*.*', included: false, served: true },
17 | { pattern: 'test/audio/**/*.*', included: false, served: true },
18 | { pattern: 'test/data/**/*.*', included: false, served: true },
19 |
20 | { pattern: 'src/*.js', type: 'module', included: false },
21 | { pattern: 'test/utils.js', type: 'module', included: false },
22 | { pattern: 'test/unit/*.spec.js', type: 'module' },
23 | { pattern: 'test/integration/*.spec.js', type: 'module' }
24 | ],
25 | browsers: [DEBUG ? 'Chrome' : 'ChromeHeadless'],
26 | proxies: {
27 | '/imgs': '/base/test/imgs',
28 | '/audio': '/base/test/audio',
29 | '/data': '/base/test/data'
30 | },
31 | reporters: ['mocha', 'coverage'],
32 | preprocessors: {
33 | 'src/**/*.js': ['coverage']
34 | },
35 | coverageReporter: {
36 | check: {
37 | emitWarning: false,
38 | global: {
39 | statements: 95,
40 | branches: 95,
41 | functions: 95,
42 | lines: 95
43 | }
44 | },
45 | dir: 'coverage/',
46 | reporters: [{ type: 'html' }, { type: 'text-summary' }]
47 | },
48 | client: {
49 | mocha: {
50 | timeout: 4000,
51 | reporter: 'html'
52 | }
53 | }
54 | });
55 | };
56 |
--------------------------------------------------------------------------------
/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDg2+nTdvsPrAiU
3 | 8pWkSxdihGZzBsFGjoaQyC2x152r1Fe2d7JjKb+vMjLBrb3Z/rdsUGBa4FaeYkwy
4 | W6HmnEckEIASmISJU0UHEwytywoU9LcWs9wfCG3VRJV4IVhrx6CZ01JzGukkLjO3
5 | XHq0HW+mdL6B7QYUMM8IqeGYS1u12Khf3sZVslxX9FA0TwyS48K9/rTw7GdeQY0v
6 | +99rOfJdgEJtOfv59UIg4CGG934Vc0o4xDvyFT0tz8Ux/hiOYOhBSOb3FSJB+6Ng
7 | xX+2FJ/OY2DE9sot7mibaCYL0sOEF9Fi76l7lFFbczIfcf+WcvVJ1Ux7i+wqZy7+
8 | 32BkwHFxAgMBAAECggEAbKkZB715eYtS4leQBMLc3BjLQU7EW4pIcPKrUkO1x/Fn
9 | KaASLmVgYhNJ/9or4op6rPbyeTfr48HwvG0Xgc+HeWAX4+ScN5hrxQ1plRqHFrVj
10 | PK9R8hUqrmLkMBc9GWhwraU3NLSOcZN6HmOsUBnheHj2DucxhtRHWBJwGB5ihS7z
11 | 2L3kLb/a+Uc/14fFDXde4QyEi7rLnNdkRIkeJaVNPnVnLyGto4TuVIj1GIJLYVNG
12 | 4zIEptSIALgFIzS5L09Cm13zJiH0TMSIuBf3SFJTJJhTQcMCzB37Lag/Hee1Ja0i
13 | bhUulCMMHquBHMW5d0Jb+qsUWiTPnNjlOCFgegPHoQKBgQDyhDejPP8/N+oDJmSi
14 | e0DRQpwqzOxwNfTmr7+kaW8cUwWJHnUr9J06BFOFNN0fWVL3CG6sSsZki9fAaOWI
15 | 0aTVJ5ufOgdsbcAoIukGFlxKTbYvLppqwOGImKudLtEMMprwzypNQotJW0c+VOiN
16 | K1sCJEVDhihvaNYIEXZWnBSD1QKBgQDtXF/ZwEBS/+pIJkg1ghdGtaNzx7F3Fm1U
17 | nfiI8MoV0hGHp8aZ0gKLFRonOr89OW3f/0LK9l6u0MFPygjkjC6yg+dlOdXmNc7U
18 | O/8b98BCkGDz4Na3zOFcJEVY6V4ZsqGfk8IGSTifvmiUSsfdXpDVolhQ9InefJh9
19 | wCOAEIOxLQKBgQDP0i/fiijtotu9gUwh1N9Rs/Qh1WQUMJjCiv7+RH+71QVcYKZm
20 | WYPWsNhlwUxwTdqD6Uz2BkoG5bOopft1CLppEz0P8OllqJNPkcMAvW6vGfMycYxQ
21 | SSO8K6B83R61hjQygkUs2gaEgV0G9Doop2ug5TYZzECgYEVxuo0fYTdPVQKBgQC3
22 | WWOxEJCfjI+sq+Wbb6ILPMPF67tqAijx9BTHszhnIp3n6/G9YDwWs3ZAV2DiKjp1
23 | jPhLT4RUBW1N9QJpiN+Jhdp4lvRjn6zkxHOLZxVcVaqOuF8kG175jgsDY0ENGK9A
24 | VSLLOERFIRAnfJxmo2W9oGoYHs1gz137xS+m/Rq9AQKBgH1IWfc4ljfv82fUOXZi
25 | Adk1uCQ0LP4S4Dyao3PCgKfYKkt/A0SrKHRhVTTRmVa13rnwj5+dVXzZXJ7K7isn
26 | yieVoBhj/LS2xUfn3opsAYfs+KXzYDm4ke3JloaUJquFYSmZCS/JC4hQ3ZyWgNAB
27 | O2Xt6sbEcWMHMJkZwn3411jp
28 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kontra",
3 | "version": "10.0.2",
4 | "description": "Kontra HTML5 game development library",
5 | "main": "kontra.js",
6 | "module": "kontra.mjs",
7 | "sideEffects": false,
8 | "files": [
9 | "kontra.js",
10 | "kontra.mjs",
11 | "kontra.min.js",
12 | "kontra.min.mjs",
13 | "kontra.d.ts"
14 | ],
15 | "scripts": {
16 | "start": "http-server -S -C cert.pem",
17 | "test": "karma start --single-run",
18 | "test:watch": "karma start",
19 | "test:permutations": "node test/permutations",
20 | "test:ts": "tsc test/typings/*.ts --noEmit",
21 | "test:debug": "karma start --debug",
22 | "eslint": "eslint ./{src,test}/",
23 | "build": "gulp build",
24 | "build:docs": "gulp build:docs",
25 | "watch": "gulp watch",
26 | "dist": "gulp dist",
27 | "release": "sh tasks/release.sh",
28 | "prepare": "husky install"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "https://github.com/straker/kontra.git"
33 | },
34 | "keywords": [
35 | "HTML5",
36 | "JavaScript",
37 | "game",
38 | "library",
39 | "js13k"
40 | ],
41 | "engines": {
42 | "node": ">=14.0.0"
43 | },
44 | "author": "Steven Lambert",
45 | "license": "MIT",
46 | "bugs": {
47 | "url": "https://github.com/straker/kontra/issues"
48 | },
49 | "homepage": "https://github.com/straker/kontra",
50 | "devDependencies": {
51 | "@rollup/stream": "^2.0.0",
52 | "chai": "^4.3.4",
53 | "coveralls": "^3.1.1",
54 | "eslint": "^8.4.1",
55 | "eslint-plugin-mocha-no-only": "^1.1.1",
56 | "glob": "^7.2.0",
57 | "gulp": "^4.0.2",
58 | "gulp-livingcss": "^5.0.0",
59 | "gulp-plumber": "^1.2.1",
60 | "gulp-preprocess": "git+https://github.com/straker/gulp-preprocess.git",
61 | "gulp-rename": "^2.0.0",
62 | "gulp-replace": "^1.1.3",
63 | "gulp-size": "^4.0.1",
64 | "gulp-terser": "^2.1.0",
65 | "http-server": "^14.0.0",
66 | "husky": "^7.0.2",
67 | "karma": "^6.3.16",
68 | "karma-chai": "^0.1.0",
69 | "karma-chrome-launcher": "^3.1.0",
70 | "karma-coverage": "^2.2.1",
71 | "karma-mocha": "^2.0.1",
72 | "karma-mocha-reporter": "^2.2.5",
73 | "karma-sinon": "^1.0.5",
74 | "lint-staged": "^13.2.1",
75 | "livingcss": "^7.0.1",
76 | "marked": "^3.0.7",
77 | "mocha": "^9.2.0",
78 | "preprocess": "git+https://github.com/straker/preprocess.git",
79 | "prettier": "^2.4.1",
80 | "rollup": "^2.58.0",
81 | "sinon": "^11.1.2",
82 | "typescript": "^4.4.4",
83 | "vinyl-source-stream": "^2.0.0"
84 | },
85 | "lint-staged": {
86 | "*.js": [
87 | "prettier --write",
88 | "eslint --fix"
89 | ]
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/core.js:
--------------------------------------------------------------------------------
1 | import { noop } from './utils.js';
2 | import { emit } from './events.js';
3 |
4 | /**
5 | * Functions for initializing the Kontra library and getting the canvas and context
6 | * objects.
7 | *
8 | * ```js
9 | * import { getCanvas, getContext, init } from 'kontra';
10 | *
11 | * let { canvas, context } = init();
12 | *
13 | * // or can get canvas and context through functions
14 | * canvas = getCanvas();
15 | * context = getContext();
16 | * ```
17 | * @sectionName Core
18 | */
19 |
20 | let canvasEl, context;
21 |
22 | // allow contextless environments, such as using ThreeJS as the main
23 | // canvas, by proxying all canvas context calls
24 | let handler = {
25 | // by using noop we can proxy both property and function calls
26 | // so neither will throw errors
27 | get(target, key) {
28 | // export for testing
29 | if (key == '_proxy') return true;
30 | return noop;
31 | }
32 | };
33 |
34 | /**
35 | * Return the canvas element.
36 | * @function getCanvas
37 | *
38 | * @returns {HTMLCanvasElement} The canvas element for the game.
39 | */
40 | export function getCanvas() {
41 | return canvasEl;
42 | }
43 |
44 | /**
45 | * Return the context object.
46 | * @function getContext
47 | *
48 | * @returns {CanvasRenderingContext2D} The context object the game draws to.
49 | */
50 | export function getContext() {
51 | return context;
52 | }
53 |
54 | /**
55 | * Initialize the library and set up the canvas. Typically you will call `init()` as the first thing and give it the canvas to use. This will allow all Kontra objects to reference the canvas when created.
56 | *
57 | * ```js
58 | * import { init } from 'kontra';
59 | *
60 | * let { canvas, context } = init('game');
61 | * ```
62 | * @function init
63 | *
64 | * @param {String|HTMLCanvasElement} [canvas] - The canvas for Kontra to use. Can either be the ID of the canvas element or the canvas element itself. Defaults to using the first canvas element on the page.
65 | * @param {Object} [options] - Game options.
66 | * @param {Boolean} [options.contextless=false] - If the game will run in an contextless environment. A contextless environment uses a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) for the `canvas` and `context` so all property and function calls will noop.
67 | *
68 | * @returns {{canvas: HTMLCanvasElement, context: CanvasRenderingContext2D}} An object with properties `canvas` and `context`. `canvas` it the canvas element for the game and `context` is the context object the game draws to.
69 | */
70 | export function init(canvas, { contextless = false } = {}) {
71 | // check if canvas is a string first, an element next, or default to
72 | // getting first canvas on page
73 | canvasEl =
74 | document.getElementById(canvas) ||
75 | canvas ||
76 | document.querySelector('canvas');
77 |
78 | if (contextless) {
79 | canvasEl = canvasEl || new Proxy({}, handler);
80 | }
81 |
82 | // @ifdef DEBUG
83 | if (!canvasEl) {
84 | throw Error('You must provide a canvas element for the game');
85 | }
86 | // @endif
87 |
88 | context = canvasEl.getContext('2d') || new Proxy({}, handler);
89 | context.imageSmoothingEnabled = false;
90 |
91 | emit('init');
92 |
93 | return { canvas: canvasEl, context };
94 | }
95 |
96 | // expose for testing
97 | export function _reset() {
98 | canvasEl = context = undefined;
99 | }
--------------------------------------------------------------------------------
/src/events.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple event system. Allows you to hook into Kontra lifecycle events or create your own, such as for [Plugins](api/plugin).
3 | *
4 | * ```js
5 | * import { on, off, emit } from 'kontra';
6 | *
7 | * function callback(a, b, c) {
8 | * console.log({a, b, c});
9 | * });
10 | *
11 | * on('myEvent', callback);
12 | * emit('myEvent', 1, 2, 3); //=> {a: 1, b: 2, c: 3}
13 | * off('myEvent', callback);
14 | * ```
15 | * @sectionName Events
16 | */
17 |
18 | // expose for testing
19 | export let callbacks = {};
20 |
21 | /**
22 | * There are currently only three lifecycle events:
23 | * - `init` - Emitted after `kontra.init()` is called.
24 | * - `tick` - Emitted every frame of [GameLoop](api/gameLoop) before the loops `update()` and `render()` functions are called.
25 | * - `assetLoaded` - Emitted after an asset has fully loaded using the asset loader. The callback function is passed the asset and the url of the asset as parameters.
26 | * @sectionName Lifecycle Events
27 | */
28 |
29 | /**
30 | * Register a callback for an event to be called whenever the event is emitted. The callback will be passed all arguments used in the `emit` call.
31 | * @function on
32 | *
33 | * @param {String} event - Name of the event.
34 | * @param {Function} callback - Function that will be called when the event is emitted.
35 | */
36 | export function on(event, callback) {
37 | callbacks[event] = callbacks[event] || [];
38 | callbacks[event].push(callback);
39 | }
40 |
41 | /**
42 | * Remove a callback for an event.
43 | * @function off
44 | *
45 | * @param {String} event - Name of the event.
46 | * @param {Function} callback - The function that was passed during registration.
47 | */
48 | export function off(event, callback) {
49 | callbacks[event] = (callbacks[event] || []).filter(
50 | fn => fn != callback
51 | );
52 | }
53 |
54 | /**
55 | * Call all callback functions for the event. All arguments will be passed to the callback functions.
56 | * @function emit
57 | *
58 | * @param {String} event - Name of the event.
59 | * @param {...*} args - Comma separated list of arguments passed to all callbacks.
60 | */
61 | export function emit(event, ...args) {
62 | (callbacks[event] || []).map(fn => fn(...args));
63 | }
64 |
65 | // expose for testing
66 | export function _reset() {
67 | Object.keys(callbacks).map(key => {
68 | delete callbacks[key];
69 | });
70 | }
71 |
--------------------------------------------------------------------------------
/src/kontra.js:
--------------------------------------------------------------------------------
1 | export { default as Animation, AnimationClass } from './animation.js';
2 | export {
3 | imageAssets,
4 | audioAssets,
5 | dataAssets,
6 | setImagePath,
7 | setAudioPath,
8 | setDataPath,
9 | loadImage,
10 | loadAudio,
11 | loadData,
12 | load
13 | } from './assets.js';
14 | export { default as Button, ButtonClass } from './button.js';
15 | export { init, getCanvas, getContext } from './core.js';
16 | export { on, off, emit } from './events.js';
17 | export { default as GameLoop } from './gameLoop.js';
18 | export {
19 | default as GameObject,
20 | GameObjectClass
21 | } from './gameObject.js';
22 | export {
23 | gamepadMap,
24 | updateGamepad,
25 | initGamepad,
26 | onGamepad,
27 | offGamepad,
28 | gamepadPressed,
29 | gamepadAxis
30 | } from './gamepad.js';
31 | export {
32 | gestureMap,
33 | initGesture,
34 | onGesture,
35 | offGesture
36 | } from './gesture.js';
37 | export { default as Grid, GridClass } from './grid.js';
38 | export {
39 | degToRad,
40 | radToDeg,
41 | angleToTarget,
42 | rotatePoint,
43 | movePoint,
44 | lerp,
45 | inverseLerp,
46 | clamp,
47 | setStoreItem,
48 | getStoreItem,
49 | collides,
50 | getWorldRect,
51 | depthSort
52 | } from './helpers.js';
53 | export { initInput, onInput, offInput } from './input.js';
54 | export {
55 | keyMap,
56 | initKeys,
57 | onKey,
58 | offKey,
59 | keyPressed
60 | } from './keyboard.js';
61 | export {
62 | registerPlugin,
63 | unregisterPlugin,
64 | extendObject
65 | } from './plugin.js';
66 | export {
67 | initPointer,
68 | getPointer,
69 | track,
70 | untrack,
71 | pointerOver,
72 | onPointer,
73 | offPointer,
74 | pointerPressed
75 | } from './pointer.js';
76 | export { default as Pool, PoolClass } from './pool.js';
77 | export { default as Quadtree, QuadtreeClass } from './quadtree.js';
78 | export { rand, randInt, getSeed, seedRand } from './random.js';
79 | export { default as Scene, SceneClass } from './scene.js';
80 | export { default as Sprite, SpriteClass } from './sprite.js';
81 | export {
82 | default as SpriteSheet,
83 | SpriteSheetClass
84 | } from './spriteSheet.js';
85 | export { default as Text, TextClass } from './text.js';
86 | export {
87 | default as TileEngine,
88 | TileEngineClass
89 | } from './tileEngine.js';
90 | export { default as Vector, VectorClass } from './vector.js';
91 | export { default } from './kontra.defaults.js';
92 |
--------------------------------------------------------------------------------
/src/random.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A pseudo-random number generator (PRNG).
3 | *
4 | * @sectionName Random
5 | */
6 | let seed;
7 |
8 | /**
9 | * Return a random number between 0 (inclusive) and 1 (exclusive).
10 | * @see https://github.com/bryc/code/blob/master/jshash/PRNGs.md#splitmix32
11 | * @function rand
12 | *
13 | * @returns {Number} Random number between 0 and <1.
14 | */
15 | export function rand() {
16 | seed ??= Date.now();
17 | seed |= 0;
18 | seed = (seed + 0x9e3779b9) | 0;
19 | let t = seed ^ (seed >>> 16);
20 | t = Math.imul(t, 0x21f0aaad);
21 | t = t ^ (t >>> 15);
22 | t = Math.imul(t, 0x735a2d97);
23 | return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;
24 | }
25 |
26 | /**
27 | * Return a random integer between a minimum (inclusive) and maximum (inclusive) integer.
28 | *
29 | * ```js
30 | * import { randInt, rand } from 'kontra';
31 | *
32 | * // random number between 10 and 20
33 | * console.log( randInt(10, 20) );
34 | *
35 | * // bias the result of the random integer to be closer
36 | * // to the max
37 | * console.log( randInt(10, 20, () => rand() ** 2) );
38 | * ```
39 | * @see https://stackoverflow.com/a/1527820/2124254
40 | * @function randInt
41 | *
42 | * @param {Number} min - Min integer.
43 | * @param {Number} max - Max integer.
44 | * @param {() => Number} [randFn] - Function that generates a random number. Useful for [biasing the random number](https://gamedev.stackexchange.com/a/116875).
45 | *
46 | * @returns {Number} Random integer between min and max values.
47 | */
48 | export function randInt(min, max, randFn = rand) {
49 | return ((randFn() * (max - min + 1)) | 0) + min;
50 | }
51 |
52 | /**
53 | * Get the current seed value of the random number generator.
54 | * @function getSeed
55 | *
56 | * @returns {Number} The seed value.
57 | */
58 | export function getSeed() {
59 | return seed;
60 | }
61 |
62 | /**
63 | * Initialize the random number generator with a given seed.
64 | *
65 | * ```js
66 | * import { seedRand, rand } from 'kontra';
67 | *
68 | * seedRand('kontra');
69 | * console.log(rand()); // => always 0.26133555523119867
70 | * ```
71 | * @see https://stackoverflow.com/a/47593316/2124254
72 | * @see https://github.com/bryc/code/blob/master/jshash/PRNGs.md
73 | *
74 | * @function seedRand
75 | *
76 | * @param {Number|String} [value=Date.now()] - Number or string to seed the random number generator.
77 | */
78 | export function seedRand(value = Date.now()) {
79 | seed = value;
80 |
81 | if (typeof value == 'string') {
82 | // create a suitable hash of the seed string using MurmurHash3
83 | // @see https://github.com/bryc/code/blob/master/jshash/PRNGs.md#addendum-a-seed-generating-functions
84 | for (
85 | var i = 0, h = 1779033703 ^ value.length;
86 | i < value.length;
87 | i++
88 | ) {
89 | (h = Math.imul(h ^ value.charCodeAt(i), 3432918353)),
90 | (h = (h << 13) | (h >>> 19));
91 | }
92 | h = Math.imul(h ^ (h >>> 16), 2246822507);
93 | h = Math.imul(h ^ (h >>> 13), 3266489909);
94 | seed = (h ^= h >>> 16) >>> 0;
95 | }
96 | }
97 |
98 | // export just for testing
99 | export function _reset() {
100 | seed = null;
101 | }
102 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { getWorldRect } from './helpers.js';
2 |
3 | export let noop = () => {};
4 |
5 | // style used for DOM nodes needed for screen readers
6 | export let srOnlyStyle =
7 | 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);';
8 | // prevent focus from scrolling the page
9 | export let focusParams = { preventScroll: true };
10 |
11 | /**
12 | * Append a node directly after the canvas and as the last element of other kontra nodes.
13 | *
14 | * @param {HTMLElement} node - Node to append.
15 | * @param {HTMLCanvasElement} canvas - Canvas to append after.
16 | */
17 | export function addToDom(node, canvas) {
18 | let container = canvas.parentNode;
19 |
20 | node.setAttribute('data-kontra', '');
21 | if (container) {
22 | let target =
23 | [
24 | ...container.querySelectorAll(':scope > [data-kontra]')
25 | ].pop() || canvas;
26 | target.after(node);
27 | } else if (canvas.nodeName == 'CANVAS') {
28 | document.body.append(node);
29 | } else {
30 | canvas.append(node);
31 | }
32 | }
33 |
34 | /**
35 | * Remove an item from an array.
36 | *
37 | * @param {*[]} array - Array to remove from.
38 | * @param {*} item - Item to remove.
39 | *
40 | * @returns {Boolean|undefined} True if the item was removed.
41 | */
42 | export function removeFromArray(array, item) {
43 | let index = array.indexOf(item);
44 | if (index != -1) {
45 | array.splice(index, 1);
46 | return true;
47 | }
48 | }
49 |
50 | /**
51 | * Detection collision between a rectangle and a circle.
52 | * @see https://yal.cc/rectangle-circle-intersection-test/
53 | *
54 | * @param {Object} rect - Rectangular object to check collision against.
55 | * @param {Object} circle - Circular object to check collision against.
56 | *
57 | * @returns {Boolean} True if objects collide.
58 | */
59 | export function circleRectCollision(circle, rect) {
60 | let { x, y, width, height } = getWorldRect(rect);
61 |
62 | // account for camera
63 | do {
64 | x -= rect.sx || 0;
65 | y -= rect.sy || 0;
66 | } while ((rect = rect.parent));
67 |
68 | let dx = circle.x - Math.max(x, Math.min(circle.x, x + width));
69 | let dy = circle.y - Math.max(y, Math.min(circle.y, y + height));
70 | return dx * dx + dy * dy < circle.radius * circle.radius;
71 | }
72 |
--------------------------------------------------------------------------------
/tasks/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # colors
5 | # @see https://stackoverflow.com/a/5947802/2124254
6 | Cyan='\033[0;36m'
7 | NC='\033[0m' # No Color
8 |
9 | versions=("major" "minor" "patch")
10 |
11 | # https://stackoverflow.com/a/8574392/2124254
12 | containsElement () {
13 | local e match="$1"
14 | shift
15 | for e; do [[ "$e" == "$match" ]] && return 0; done
16 | return 1
17 | }
18 |
19 | # enforce passing an argument
20 | # https://stackoverflow.com/a/22725973/2124254
21 | if (( $# == 0 )) || ( ! containsElement $1 "${versions[@]}" ); then
22 | echo "usage: sh tasks/release.sh [major | minor | patch]"
23 | exit 1;
24 | fi
25 |
26 | printf "\n${Cyan}Releasing lib${NC}\n"
27 | npm version $1
28 | version=$(node -p "require('./package.json').version")
29 |
30 | npm run dist
31 | npm publish
32 |
33 | # push version changes to service-worker before updating docs
34 | # https://github.com/straker/kontra/issues/410
35 | git add .
36 | git commit -m "docs: update service-worker version"
37 | git push origin main
38 |
39 | printf "\n${Cyan}Releasing docs${NC}\n"
40 | # rebuild docs to generate download page with newest version
41 | npm run build:docs
42 | cd docs
43 | find . -name "*.html" | cpio -pvd ../tmp &>/dev/null
44 | cp -r assets ../tmp/assets
45 | cd ..
46 | git checkout gh-pages
47 | ls | grep -v tmp | grep -v node_modules | xargs rm -rf
48 | mv tmp/* .
49 | rm -rf tmp
50 | git add .
51 | git commit -m "docs: release $version"
52 | git push origin gh-pages
53 | git checkout main
--------------------------------------------------------------------------------
/tasks/ts-template.hbs:
--------------------------------------------------------------------------------
1 | declare namespace kontra {
2 | {{#each allSections}}
3 | {{#if class}}
4 | {{! copied from Typescript es5 definitions of standard API functions }}
5 | {{! @see https://github.com/microsoft/TypeScript/blob/master/lib/lib.es5.d.ts#L272-L316 }}
6 | interface {{class}}{{#if extends}} extends {{extends}}{{/if}} {
7 | {{#each children}}
8 | {{#if function}}
9 | {{name}}({{{params}}}): {{{returns}}};
10 | {{/if}}
11 | {{#if property}}
12 | {{#if readonly}}readonly {{/if}}{{property.name}}: {{{property.type}}};
13 | {{/if}}
14 | {{/each}}
15 | }
16 | interface {{class}}Constructor {
17 | new({{{params}}}): {{class}};
18 | }
19 | var {{class}}Class: {{class}}Constructor
20 | function {{class}}({{{params}}}): {{class}};
21 | {{/if}}
22 | {{#unless memberof}}
23 | {{#if function}}
24 | function {{name}}({{{params}}}): {{{returns}}};
25 | {{/if}}
26 | {{#if property}}
27 | var {{property.name}}: {{{property.type}}};
28 | {{/if}}
29 | {{/unless}}
30 | {{/each}}
31 | }
32 |
33 | export = kontra
--------------------------------------------------------------------------------
/test/audio/shoot.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/test/audio/shoot.mp3
--------------------------------------------------------------------------------
/test/audio/shoot.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/test/audio/shoot.ogg
--------------------------------------------------------------------------------
/test/data/source.json:
--------------------------------------------------------------------------------
1 | {
2 | "image": "/imgs/bullet.png"
3 | }
--------------------------------------------------------------------------------
/test/data/test.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "bar",
3 | "lorium": "ipsum"
4 | }
--------------------------------------------------------------------------------
/test/data/test.txt:
--------------------------------------------------------------------------------
1 | Hello world!
--------------------------------------------------------------------------------
/test/data/tileset/bullet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/test/data/tileset/bullet.png
--------------------------------------------------------------------------------
/test/data/tileset/readme.txt:
--------------------------------------------------------------------------------
1 | In order to property fail the tileEngine integration test of loading a tileset image from a json file, we need a path that will not resolve when the tileset does not resolve properly. E.g. if the tileset image is `../imgs/bullet.png` and tileset data url incorrectly resoles to `https://localhost:9090/context.html`, that will resolve to `https://localhost:9090/imgs/bullet.png` which is a correct url, but an incorrect resolve.
--------------------------------------------------------------------------------
/test/data/tileset/tileset.json:
--------------------------------------------------------------------------------
1 | { "height":9,
2 | "layers":[
3 | {
4 | "data":[203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203],
5 | "height":9,
6 | "name":"base",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":true,
10 | "width":9,
11 | "x":0,
12 | "y":0
13 | },
14 | {
15 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 0, 0, 0, 0, 6, 27, 24, 24, 25, 0, 0, 0, 0, 23, 24, 24, 24, 26, 8, 0, 0, 0, 23, 24, 24, 24, 24, 26, 8, 0, 0, 23, 24, 24, 24, 24, 24, 25, 0, 0, 40, 41, 41, 10, 24, 24, 25, 0, 0, 0, 0, 0, 40, 41, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
16 | "height":9,
17 | "name":"grass",
18 | "opacity":1,
19 | "type":"tilelayer",
20 | "visible":true,
21 | "width":9,
22 | "x":0,
23 | "y":0
24 | },
25 | {
26 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | "height":9,
28 | "name":"decoration",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":9,
33 | "x":0,
34 | "y":0
35 | }],
36 | "orientation":"orthogonal",
37 | "properties":
38 | {
39 |
40 | },
41 | "tileheight":64,
42 | "tilesets":[
43 | {
44 | "firstgid":1,
45 | "image":"./bullet.png",
46 | "imageheight":768,
47 | "imagewidth":1088,
48 | "margin":0,
49 | "name":"bullet",
50 | "properties":
51 | {
52 |
53 | },
54 | "spacing":0,
55 | "tileheight":64,
56 | "tilewidth":64
57 | }],
58 | "tilewidth":64,
59 | "version":1,
60 | "width":9
61 | }
--------------------------------------------------------------------------------
/test/imgs/bullet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/straker/kontra/3066be56c47ca4c885e72ad3aefd513f42bda244/test/imgs/bullet.png
--------------------------------------------------------------------------------
/test/integration/core.spec.js:
--------------------------------------------------------------------------------
1 | import Sprite from '../../src/sprite.js';
2 | import TileEngine from '../../src/tileEngine.js';
3 | import GameLoop from '../../src/gameLoop.js';
4 | import Text from '../../src/text.js';
5 | import Scene from '../../src/scene.js';
6 | import { _reset, init } from '../../src/core.js';
7 | import { noop } from '../../src/utils.js';
8 |
9 | describe('core integration', () => {
10 | beforeEach(() => {
11 | _reset();
12 | });
13 |
14 | it('should allow calling init after creating objects', () => {
15 | let sprite = Sprite({
16 | x: 10,
17 | y: 20,
18 | width: 10,
19 | height: 20,
20 | color: 'red'
21 | });
22 | let loop = GameLoop({
23 | update: noop,
24 | render: noop
25 | });
26 | let text = Text({ text: 'Hello World' });
27 | let tileEngine = TileEngine({
28 | tilewidth: 10,
29 | tileheight: 10,
30 | width: 50,
31 | height: 50,
32 | tilesets: [
33 | {
34 | image: new Image()
35 | }
36 | ],
37 | layers: [
38 | {
39 | name: 'test',
40 | data: [0, 0, 1, 0, 0]
41 | }
42 | ]
43 | });
44 |
45 | let scene = Scene({
46 | id: 'myId',
47 | objects: [sprite, text]
48 | });
49 |
50 | expect(() => scene.lookAt(sprite)).to.not.throw();
51 |
52 | let canvas = document.createElement('canvas');
53 | canvas.width = canvas.height = 600;
54 | init(canvas);
55 |
56 | expect(() => sprite.render()).to.not.throw();
57 | expect(() => text.render()).to.not.throw();
58 | expect(() => tileEngine.render()).to.not.throw();
59 | expect(() => scene.render()).to.not.throw();
60 |
61 | loop._last = performance.now() - (1e3 / 60) * 2.5;
62 | expect(() => loop._frame()).to.not.throw();
63 | });
64 | });
--------------------------------------------------------------------------------
/test/integration/scene.spec.js:
--------------------------------------------------------------------------------
1 | import Scene from '../../src/scene.js';
2 | import Sprite from '../../src/sprite.js';
3 | import TileEngine from '../../src/tileEngine.js';
4 | import { init, getCanvas } from '../../src/core.js';
5 | import { depthSort } from '../../src/helpers.js';
6 | import * as pointer from '../../src/pointer.js';
7 | import { noop } from '../../src/utils.js';
8 | import { emit } from '../../src/events.js';
9 |
10 | describe('scene integration', () => {
11 | before(() => {
12 | if (!getCanvas()) {
13 | let canvas = document.createElement('canvas');
14 | canvas.width = canvas.height = 600;
15 | init(canvas);
16 | }
17 | });
18 |
19 | it('should render a tileEngine', () => {
20 | let spy = sinon.spy();
21 | let tileEngine = TileEngine({
22 | width: 10,
23 | height: 12,
24 | tilewidth: 32,
25 | tileheight: 32,
26 | tilesets: [],
27 | render: spy
28 | });
29 |
30 | let scene = Scene({
31 | id: 'myId',
32 | objects: [tileEngine]
33 | });
34 |
35 | scene.render();
36 |
37 | expect(spy.called).to.be.true;
38 | });
39 |
40 | it('should work with helpers.depthSort', () => {
41 | let objects = [];
42 | let spies = [];
43 | for (let i = 5; i > 0; i--) {
44 | let spy = sinon.spy();
45 | spies.push(spy);
46 | objects.push(
47 | Sprite({
48 | id: i,
49 | x: 5,
50 | y: i * 10,
51 | width: 15,
52 | height: 25,
53 | render: spy
54 | })
55 | );
56 | }
57 |
58 | let scene = Scene({
59 | id: 'myId',
60 | objects,
61 | sortFunction: depthSort
62 | });
63 | scene.render();
64 |
65 | expect(spies[4].calledBefore(spies[3])).to.be.true;
66 | expect(spies[3].calledBefore(spies[2])).to.be.true;
67 | expect(spies[2].calledBefore(spies[1])).to.be.true;
68 | expect(spies[1].calledBefore(spies[0])).to.be.true;
69 | });
70 |
71 | it('should correctly track objects with pointer when camera is moved', () => {
72 | let pntr = pointer.initPointer({ radius: 1 });
73 | let object = {
74 | x: 100,
75 | y: 50,
76 | width: 10,
77 | height: 20,
78 | render: noop
79 | };
80 | let scene = Scene({
81 | id: 'myId',
82 | objects: [object]
83 | });
84 | pointer.track(object);
85 | object.render();
86 | emit('tick');
87 |
88 | pntr.x = 105;
89 | pntr.y = 55;
90 | expect(pointer.pointerOver(object)).to.equal(true);
91 |
92 | scene.camera.x += 100;
93 | expect(pointer.pointerOver(object)).to.equal(false);
94 |
95 | pntr.x = 5;
96 | expect(pointer.pointerOver(object)).to.equal(true);
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/test/integration/sprite.spec.js:
--------------------------------------------------------------------------------
1 | import Sprite from '../../src/sprite.js';
2 | import SpriteSheet from '../../src/spriteSheet.js';
3 | import { track, initPointer } from '../../src/pointer.js';
4 |
5 | describe('sprite integration', () => {
6 | it('should clone spriteSheet animations to prevent frame corruption ', () => {
7 | let spriteSheet = SpriteSheet({
8 | image: new Image(100, 200),
9 | frameWidth: 10,
10 | frameHeight: 10,
11 | animations: {
12 | walk: {
13 | frames: '1..10',
14 | frameRate: 30
15 | }
16 | }
17 | });
18 |
19 | let sprite1 = Sprite({
20 | animations: spriteSheet.animations
21 | });
22 | let sprite2 = Sprite({
23 | animations: spriteSheet.animations
24 | });
25 | let sprite3 = Sprite();
26 | sprite3.animations = spriteSheet.animations;
27 |
28 | expect(sprite1.animations.walk).to.not.equal(
29 | sprite2.animations.walk
30 | );
31 | expect(sprite1.animations.walk).to.not.equal(
32 | sprite3.animations.walk
33 | );
34 | expect(sprite2.animations.walk).to.not.equal(
35 | sprite3.animations.walk
36 | );
37 |
38 | for (let i = 0; i < 7; i++) {
39 | sprite1.update();
40 | }
41 |
42 | expect(sprite1.animations.walk._f).to.equal(3);
43 | expect(sprite2.animations.walk._f).to.equal(0);
44 | expect(sprite3.animations.walk._f).to.equal(0);
45 | });
46 |
47 | it('should not corrupt frames when playing animation', () => {
48 | let spriteSheet = SpriteSheet({
49 | image: new Image(100, 200),
50 | frameWidth: 10,
51 | frameHeight: 10,
52 | animations: {
53 | walk: {
54 | frames: '1..10',
55 | frameRate: 30
56 | }
57 | }
58 | });
59 |
60 | let sprite1 = Sprite({
61 | animations: spriteSheet.animations
62 | });
63 | let sprite2 = Sprite({
64 | animations: spriteSheet.animations
65 | });
66 | let sprite3 = Sprite();
67 | sprite3.animations = spriteSheet.animations;
68 |
69 | sprite1.playAnimation('walk');
70 | sprite2.playAnimation('walk');
71 | sprite3.playAnimation('walk');
72 |
73 | for (let i = 0; i < 7; i++) {
74 | sprite1.update();
75 | }
76 |
77 | expect(sprite1.animations.walk._f).to.equal(3);
78 | expect(sprite2.animations.walk._f).to.equal(0);
79 | expect(sprite3.animations.walk._f).to.equal(0);
80 | });
81 |
82 | it('should render when tracked by pointer', () => {
83 | initPointer();
84 | let spy = sinon.spy();
85 |
86 | let sprite = Sprite({
87 | x: 100,
88 | y: 200,
89 | color: 'red',
90 | render: spy
91 | });
92 |
93 | track(sprite);
94 | sprite.render();
95 |
96 | // retain _r as radius
97 | expect(typeof sprite._r).to.not.equal('function');
98 |
99 | // retain _rf as render function
100 | expect(sprite._rf).to.equal(spy);
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/test/integration/vector.spec.js:
--------------------------------------------------------------------------------
1 | import Vector from '../../src/vector.js';
2 | import { init, getCanvas } from '../../src/core.js';
3 | import { rotatePoint, movePoint } from '../../src/helpers.js';
4 |
5 | describe('vector integration', () => {
6 | before(() => {
7 | if (!getCanvas()) {
8 | let canvas = document.createElement('canvas');
9 | canvas.width = canvas.height = 600;
10 | init(canvas);
11 | }
12 | });
13 |
14 | it('should set from rotatePoint', () => {
15 | let vector = Vector(rotatePoint({ x: 10, y: 10 }, Math.PI));
16 |
17 | expect(vector.x).to.be.closeTo(-10, 0.1);
18 | expect(vector.y).to.be.closeTo(-10, 0.1);
19 | });
20 |
21 | it('should set from movePoint', () => {
22 | let vector = Vector(movePoint({ x: 10, y: 10 }, 0, 10));
23 |
24 | expect(vector.x).to.equal(20);
25 | expect(vector.y).to.equal(10);
26 | });
27 | });
--------------------------------------------------------------------------------
/test/permutations/karma.conf.template.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = function (config) {
4 | config.set({
5 | basePath: '',
6 | singleRun: true,
7 | autoWatch: true,
8 | frameworks: ['mocha', 'chai', 'sinon'],
9 | files: [
10 | {
11 | pattern: path.join(__dirname, '__option__.spec.js'),
12 | type: 'module'
13 | }
14 | ],
15 | proxies: {
16 | '/src': path.join('/absolute', __dirname, '../../src')
17 | },
18 | browsers: ['ChromeHeadless'],
19 | reporters: ['mocha']
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | // permutations script prepends this file with the test file
2 | // so we need to rename this import so other files that import
3 | // it don't import by the same name
4 | import {
5 | init as initCore,
6 | _reset as resetCore
7 | } from '../src/core.js';
8 | import { _reset as resetAssets } from '../src/assets.js';
9 | import { _reset as resetEvents } from '../src/events.js';
10 | import { _reset as resetGesture } from '../src/gesture.js';
11 | import { _reset as seedReset } from '../src/random.js';
12 |
13 | // ensure canvas exists before each test
14 | function setup() {
15 | let canvas = document.createElement('canvas');
16 | canvas.id = 'mainCanvas';
17 | canvas.width = canvas.height = 600;
18 | document.body.appendChild(canvas);
19 | initCore(canvas);
20 | }
21 |
22 | beforeEach(() => {
23 | setup();
24 | });
25 |
26 | afterEach(() => {
27 | document
28 | .querySelectorAll('canvas')
29 | .forEach(canvas => canvas.remove());
30 | document
31 | .querySelectorAll('[data-kontra]')
32 | .forEach(node => node.remove());
33 |
34 | sinon.restore();
35 |
36 | resetAssets();
37 | resetCore();
38 | resetEvents();
39 | resetGesture();
40 | seedReset();
41 | });
42 |
--------------------------------------------------------------------------------
/test/typings/animation.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | // animation
4 | let spriteSheet: kontra.SpriteSheet = kontra.SpriteSheet({
5 | image: kontra.imageAssets.character_walk_sheet,
6 | frameWidth: 72,
7 | frameHeight: 97
8 | });
9 |
10 | let animation: kontra.Animation = kontra.Animation({
11 | name: 'anim',
12 | spriteSheet: spriteSheet,
13 | frames: [1,2,3,4],
14 | frameRate: 35
15 | });
16 |
17 | animation.update();
18 | animation.update(1/60);
19 | animation.render({x: 10, y: 20});
20 | animation.reset();
21 | animation.stop();
22 | animation.start();
23 |
24 | let spriteSheetAnim: kontra.SpriteSheet = animation.spriteSheet;
25 | let frames: number[] = animation.frames;
26 | let frameRate: number = animation.frameRate;
27 | let loop: boolean = animation.loop;
28 | let width: number = animation.width;
29 | let height: number = animation.height;
30 | let margin: number = animation.margin;
31 | let stopped: boolean = animation.isStopped;
32 | let name: string = animation.name;
33 |
34 | // clone
35 | let clone: kontra.Animation = animation.clone();
36 |
37 | // loop
38 | let loopAnim = kontra.Animation({
39 | spriteSheet: spriteSheet,
40 | frames: [1,2,3,4],
41 | frameRate: 35,
42 | loop: false
43 | });
44 |
45 | // render props
46 | let context = document.createElement('canvas').getContext('2d');
47 | loopAnim.render({
48 | x: 10,
49 | y: 20,
50 | width: 100,
51 | height: 100,
52 | context: context
53 | });
54 |
55 | // extends
56 | class CustomAnimation extends kontra.AnimationClass {}
57 | let myAnim = new CustomAnimation({
58 | spriteSheet: spriteSheet,
59 | frames: [1,2,3,4],
60 | frameRate: 35
61 | });
62 | myAnim.update();
--------------------------------------------------------------------------------
/test/typings/assets.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let img: HTMLImageElement = kontra.imageAssets.image;
4 | let audio: HTMLAudioElement = kontra.audioAssets.audio;
5 | let data: string = kontra.dataAssets.myStr;
6 | let json: object = kontra.dataAssets.json;
7 |
8 | kontra.setImagePath('/imgs');
9 | kontra.setAudioPath('/audio');
10 | kontra.setDataPath('/data');
11 |
12 | kontra.loadImage('./path/to/img.png')
13 | .then(img => {
14 | let image: HTMLImageElement = img;
15 | });
16 | kontra.loadAudio('./path/to/audio.mp3')
17 | .then(audio => {
18 | let song: HTMLAudioElement = audio;
19 | });
20 | kontra.loadAudio(['./path/to/audio.mp3', './path/to/audio.wav'])
21 | .then(audio => {
22 | let song: HTMLAudioElement = audio;
23 | });
24 | kontra.loadData('./path/to/data.txt')
25 | .then(data => {
26 | let txt: string = data;
27 | });
28 | kontra.loadData('./path/to/data.json')
29 | .then(data => {
30 | let json: object = data;
31 | });
32 |
33 | kontra.load('./path/to/img.png')
34 | .then(assets => {
35 | let image: HTMLImageElement = assets[0];
36 | });
37 | kontra.load(
38 | './path/to/img.png',
39 | ['./path/to/audio.mp3', './path/to/audio.wav'],
40 | './path/to/data.txt',
41 | './path/to/data.json'
42 | )
43 | .then(assets => {
44 | let image: HTMLImageElement = assets[0];
45 | let audio: HTMLAudioElement = assets[1];
46 | let data: string = assets[2];
47 | let json: object = assets[3];
48 | });
--------------------------------------------------------------------------------
/test/typings/button.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let button: kontra.Button = kontra.Button({
4 | padX: 0,
5 | padY: 10,
6 | text: {
7 | text: 'Hello World!',
8 | color: 'black',
9 | font: '32px Arial',
10 | align: {x: 0.5, y: 0.5}
11 | },
12 | });
13 |
14 | let str: string = button.text;
15 | let padX: number = button.padX;
16 | let padY: number = button.padY;
17 | let text: kontra.Text = button.textNode;
18 | let disabled: boolean = button.disabled;
19 | let focused: boolean = button.focused;
20 | let hovered: boolean = button.hovered;
21 | let pressed: boolean = button.pressed;
22 | let node: HTMLButtonElement = button.node;
23 |
24 | button.enable();
25 | button.disable();
26 | button.focus();
27 | button.blur();
28 | button.destroy();
29 | button.onEnable();
30 | button.onDisable();
31 | button.onFocus();
32 | button.onBlur();
33 |
34 | // inheritance
35 | button.x += 20;
36 | button.rotation = Math.PI;
37 | button.advance();
38 | button.render();
39 |
40 | // options
41 | kontra.Button({
42 | x: 10,
43 | y: 20,
44 | text: {
45 | text: 'Hello World!',
46 | color: 'black',
47 | font: '32px Arial',
48 | textAlign: 'right',
49 | width: 200
50 | },
51 | onEnable() {},
52 | onDisable() {},
53 | onFocus() {},
54 | onBlur() {},
55 | onUp() {}
56 | });
57 |
58 | // extends
59 | class CustomButton extends kontra.ButtonClass {}
60 | let myButton = new CustomButton();
61 | myButton.enable();
--------------------------------------------------------------------------------
/test/typings/core.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let game: HTMLCanvasElement = kontra.getCanvas();
4 | let ctx: CanvasRenderingContext2D = kontra.getContext();
5 |
6 | // init
7 | kontra.init('#game');
8 | kontra.init(game);
9 | kontra.init();
10 |
11 | // return
12 | let {
13 | canvas: HTMLCanvasElement,
14 | context: CanvasRenderingContext2D
15 | } = kontra.init();
--------------------------------------------------------------------------------
/test/typings/events.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | kontra.on('myEvent', () => {
4 | console.log('fired!');
5 | });
6 | kontra.off('myEvent', () => {});
7 | kontra.emit('myEvent');
8 |
9 | // args
10 | kontra.on('myEvent', (num, str, bool, obj) => {
11 | console.log({num, str, bool, obj});
12 | });
13 | kontra.emit('myEvent', 1, 'string', true, {});
--------------------------------------------------------------------------------
/test/typings/gameLoop.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let gameLoop: kontra.GameLoop = kontra.GameLoop({
4 | update: () => {},
5 | render: () => {}
6 | });
7 |
8 | let stopped: boolean = gameLoop.isStopped;
9 | gameLoop.stop();
10 | gameLoop.start();
11 | gameLoop.update();
12 | gameLoop.update(1/60);
13 | gameLoop.render();
14 |
15 | // options
16 | let otherLoop = kontra.GameLoop({
17 | update: () => {},
18 | render: () => {},
19 | clearCanvas: false,
20 | fps: 20,
21 | context: document.createElement('canvas').getContext('2d'),
22 | blur: true
23 | });
--------------------------------------------------------------------------------
/test/typings/gameObject.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let gameObject: kontra.GameObject = kontra.GameObject();
4 |
5 | let position: kontra.Vector = gameObject.position;
6 | let velocity: kontra.Vector = gameObject.velocity;
7 | let acceleration: kontra.Vector = gameObject.acceleration;
8 | let width: number = gameObject.width;
9 | let height: number = gameObject.height;
10 | let context: CanvasRenderingContext2D = gameObject.context;
11 | let children: kontra.GameObject[] = gameObject.children;
12 | let rotation: number = gameObject.rotation;
13 | let drotation: number = gameObject.drotation;
14 | let ddrotation: number = gameObject.ddrotation;
15 | let ttl: number = gameObject.ttl;
16 | let anchor: {x: number, y: number} = gameObject.anchor;
17 | let sx: number = gameObject.sx;
18 | let sy: number = gameObject.sy;
19 | let x: number = gameObject.x;
20 | let y: number = gameObject.y;
21 | let dx: number = gameObject.dx;
22 | let dy: number = gameObject.dy;
23 | let ddx: number = gameObject.ddx;
24 | let ddy: number = gameObject.ddy;
25 | let scaleX: number = gameObject.scaleX;
26 | let scaleY: number = gameObject.scaleY;
27 | let opacity: number = gameObject.opacity;
28 | let world: {
29 | x: number,
30 | y: number,
31 | width: number,
32 | height: number,
33 | rotation: number,
34 | opacity: number,
35 | scaleX: number,
36 | scaleY: number
37 | } = gameObject.world;
38 |
39 | let alive: boolean = gameObject.isAlive();
40 | gameObject.addChild(kontra.GameObject());
41 | gameObject.addChild(kontra.GameObject(), kontra.GameObject());
42 | gameObject.addChild([kontra.GameObject(), kontra.GameObject()]);
43 | gameObject.removeChild(kontra.GameObject());
44 | gameObject.removeChild(kontra.GameObject(), kontra.GameObject());
45 | gameObject.removeChild([kontra.GameObject(), kontra.GameObject()]);
46 | gameObject.update();
47 | gameObject.update(1/60);
48 | gameObject.advance();
49 | gameObject.advance(1/60);
50 | gameObject.render();
51 | gameObject.draw();
52 | gameObject.setScale(1);
53 | gameObject.setScale(1, 2);
54 |
55 | // options
56 | kontra.GameObject({
57 | x: 10,
58 | y: 20,
59 | width: 100,
60 | height: 100,
61 | dx: 2,
62 | dy: 2,
63 | ddx: 2,
64 | ddy: 2,
65 | ttl: 10,
66 | rotation: 10,
67 | drotation: 10,
68 | ddrotation: 12,
69 | anchor: {x: 2, y: 2},
70 | scaleX: 2,
71 | scaleY: 2,
72 | opacity: 0.5,
73 | context: document.createElement('canvas').getContext('2d'),
74 | update() {},
75 | render() {}
76 | });
77 |
78 | // custom props
79 | kontra.GameObject({
80 | name: 'myObject'
81 | });
82 |
83 | // extends
84 | class CustomGameObject extends kontra.GameObjectClass {}
85 | let myGameObj = new CustomGameObject();
86 | myGameObj.render();
--------------------------------------------------------------------------------
/test/typings/gamepad.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let map: object = kontra.gamepadMap;
4 | kontra.gamepadMap[0] = 'A';
5 |
6 | kontra.initGamepad();
7 | kontra.updateGamepad();
8 |
9 | kontra.onGamepad('south', (gamepad: Gamepad, button: GamepadButton) => {
10 | console.log('south pressed');
11 | });
12 | kontra.onGamepad(['south', 'north', 'east'], () => {
13 | console.log('south, north, or east pressed');
14 | });
15 | kontra.onGamepad(['south', 'north', 'east'], () => {
16 | console.log('south, north, or east pressed');
17 | }, {
18 | gamepad: 1,
19 | handler: 'gamepaddown'
20 | });
21 | kontra.onGamepad(['south', 'north', 'east'], () => {
22 | console.log('south, north, or east pressed');
23 | }, {
24 | handler: 'gamepadup'
25 | });
26 |
27 | kontra.offGamepad('south');
28 | kontra.offGamepad(['south', 'north', 'east']);
29 | kontra.offGamepad(['south', 'north', 'east'], {
30 | gamepad: 1,
31 | handler: 'gamepaddown'
32 | });
33 | kontra.offGamepad(['south', 'north', 'east'], {
34 | handler: 'gamepadup'
35 | });
36 |
37 | kontra.gamepadPressed('south');
38 | kontra.gamepadPressed('south', { gamepad: 1 });
39 |
40 | kontra.gamepadAxis('leftstickx', 0);
--------------------------------------------------------------------------------
/test/typings/gesture.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | kontra.initGesture();
4 |
5 | let map: object = kontra.gestureMap;
6 | kontra.gestureMap.pan = {
7 | touches: 1,
8 | threshold: 10,
9 | touchstart(touches: object) {
10 | console.log('touchstart');
11 | },
12 | touchmove() {
13 | console.log('touchmove');
14 | },
15 | touchend() {
16 | console.log('touchend');
17 | }
18 | };
19 | kontra.gestureMap.foo = {
20 | touches: 2,
21 | touchstart(touches: object) {
22 | console.log('touchstart');
23 | }
24 | }
25 | kontra.gestureMap.bar = {
26 | touches: 2,
27 | touchmove(touches: object) {
28 | console.log('touchmove');
29 | }
30 | }
31 | kontra.gestureMap.baz = {
32 | touches: 2,
33 | touchend(touches: object) {
34 | console.log('touchend');
35 | }
36 | }
37 |
38 | kontra.onGesture('panleft', (evt: TouchEvent, touches: object) => {
39 | console.log('panleft');
40 | });
41 | kontra.onGesture(['panleft', 'swiperight'], () => {})
42 |
43 | kontra.offGamepad('panleft');
44 | kontra.offGamepad(['panleft', 'swiperight']);
--------------------------------------------------------------------------------
/test/typings/grid.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let grid = kontra.Grid();
4 |
5 | let flow: string = grid.flow;
6 | let align: string | string[] = grid.align;
7 | let justify: string | string[] = grid.justify;
8 | let colGap: number | number[] = grid.colGap;
9 | let rowGap: number | number[] = grid.rowGap;
10 | let numRows: number = grid.numRows;
11 | let dir: string = grid.dir;
12 | let breakpoints: {metric: Function, callback: Function}[] = grid.breakpoints;
13 |
14 | grid.destroy();
15 |
16 | // inheritance
17 | grid.x += 20;
18 | grid.rotation = Math.PI;
19 | grid.advance();
20 | grid.render();
21 |
22 | // options
23 | kontra.Grid({
24 | flow: 'grid',
25 | align: 'center',
26 | justify: 'center',
27 | colGap: 10,
28 | rowGap: 10,
29 | numCols: 2,
30 | dir: 'rtl',
31 | breakpoints: [{
32 | metric() { return true },
33 | callback() { this.numCols = 1 }
34 | }]
35 | });
36 |
37 | // gap arrays
38 | kontra.Grid({
39 | colGap: [10],
40 | rowGap: [10],
41 | });
42 |
43 | // alignment arrays
44 | kontra.Grid({
45 | align: ['end', 'center'],
46 | justify: ['end', 'center'],
47 | });
48 |
49 | // extends
50 | class CustomGrid extends kontra.GridClass {}
51 | let myGrid = new CustomGrid();
52 | myGrid.destroy();
--------------------------------------------------------------------------------
/test/typings/helpers.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let degToRad: number = kontra.degToRad(22.35);
4 | let radToDeg: number = kontra.radToDeg(0.39);
5 |
6 | let sourceSprite = kontra.Sprite({x: 0, y: 0});
7 | let targetSprite = kontra.Sprite({x: 10, y: 10});
8 | let angleToTargetXY: number = kontra.angleToTarget({x: 0, y: 0}, {x: 10, y: 10});
9 | let angleToTargetSprite = kontra.angleToTarget(sourceSprite, targetSprite);
10 |
11 | let point: {x: number, y: number} = kontra.rotatePoint({x: 0, y: 0}, Math.PI);
12 |
13 | let lerp: number = kontra.lerp(10, 20, 0.5);
14 | let inverseLerp: number = kontra.inverseLerp(10, 20, 15);
15 | let clamp: number = kontra.clamp(10, 20, 30);
16 |
17 | kontra.setStoreItem('key', true);
18 | let item: boolean = kontra.getStoreItem('key');
19 |
20 | let collision: boolean = kontra.collides({
21 | x: 10,
22 | y: 20,
23 | width: 100,
24 | height: 100
25 | }, {
26 | x: 20,
27 | y: 40,
28 | width: 25,
29 | height: 25
30 | });
31 |
32 | // sprites
33 | let sprite1 = kontra.Sprite();
34 | let sprite2 = kontra.Sprite();
35 |
36 | let theyCollides = kontra.collides(sprite1, sprite2);
37 |
38 | // rotation
39 | sprite1.rotation = Math.PI;
40 | let nope = kontra.collides(sprite1, sprite2);
41 |
42 | // anchor
43 | sprite1.anchor = {x: 0.5, y: 0.5};
44 | let withAnchor = kontra.collides(sprite1, sprite2);
45 |
46 | // scale
47 | sprite1.scale = {x: 2, y: 2};
48 | let withScale = kontra.collides(sprite1, sprite2);
49 |
50 | let rect = kontra.getWorldRect({
51 | x: 10,
52 | y: 20,
53 | width: 100,
54 | height: 100
55 | });
56 |
57 | // sprites
58 | rect = kontra.getWorldRect(sprite1);
59 |
60 | // tileEngines
61 | let tileEngine = kontra.TileEngine({
62 | width: 10,
63 | height: 12,
64 | tilewidth: 32,
65 | tileheight: 32,
66 | tilesets: [],
67 | layers: []
68 | });
69 | rect = kontra.getWorldRect(tileEngine);
70 |
71 | let sortValue = kontra.depthSort({
72 | x: 10,
73 | y: 20,
74 | width: 100,
75 | height: 100
76 | }, {
77 | x: 10,
78 | y: 20,
79 | width: 100,
80 | height: 100
81 | });
82 |
83 | // sprites
84 | sortValue = kontra.depthSort(sprite1, sprite2);
85 |
86 | // options
87 | sortValue = kontra.depthSort(sprite1, sprite2, 'x');
--------------------------------------------------------------------------------
/test/typings/input.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | kontra.initInput();
4 |
5 | kontra.onInput('south', () => {
6 | console.log('south gamepad pressed');
7 | });
8 | kontra.onInput(['arrowleft', 'south'], () => {});
9 | kontra.onInput('south', () => {}, { gamepad: { gamepad: 1 }});
10 | kontra.onInput('arrowleft', () => {}, { key: { handler: 'keydown' }});
11 |
12 | kontra.offInput('south');
13 | kontra.offInput(['south', 'arrowleft']);
14 | kontra.offInput('south', { gamepad: { gamepad: 1 }});
15 | kontra.offInput('arrowleft', { key: { handler: 'keydown' }});
16 |
--------------------------------------------------------------------------------
/test/typings/keyboard.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let map: object = kontra.keyMap;
4 | kontra.keyMap.namedKey = 'namedKey';
5 |
6 | kontra.initKeys();
7 |
8 | kontra.onKey('a', (evt: KeyboardEvent) => {
9 | console.log('a pressed');
10 | });
11 | kontra.onKey(['a', 'b', 'c'], () => {
12 | console.log('a, b, or c pressed');
13 | });
14 | kontra.onKey(['a', 'b', 'c'], () => {
15 | console.log('a, b, or c pressed');
16 | }, {
17 | preventDefault: true,
18 | handler: 'keydown'
19 | });
20 | kontra.onKey(['a', 'b', 'c'], () => {
21 | console.log('a, b, or c pressed');
22 | }, {
23 | handler: 'keyup'
24 | });
25 |
26 |
27 | kontra.offKey('a');
28 | kontra.offKey(['a', 'b', 'c']);
29 |
30 | let pressed: boolean = kontra.keyPressed('a');
31 | kontra.keyPressed(['a', 'b', 'c']);
--------------------------------------------------------------------------------
/test/typings/pointer.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let pointer = kontra.initPointer();
4 |
5 | let x: number = pointer.x;
6 | let y: number = pointer.y;
7 | let radius: number = pointer.radius;
8 |
9 | kontra.track({one: 1});
10 | kontra.track({one: 1}, {two: 2});
11 | kontra.track([{one: 1}, {two: 2}]);
12 |
13 | kontra.untrack({one: 1});
14 | kontra.untrack({one: 1}, {two: 2});
15 | kontra.untrack([{one: 1}, {two: 2}]);
16 |
17 | let over: boolean = kontra.pointerOver({one: 1});
18 |
19 | kontra.onPointer('down', (evt: MouseEvent, object: object) => {
20 | let target = evt.target;
21 | if (object) {
22 | console.log('over!');
23 | }
24 | });
25 | kontra.onPointer('down', (evt: MouseEvent) => {
26 | let target = evt.target;
27 | console.log('no object');
28 | });
29 | kontra.onPointer('up', () => {});
30 |
31 | kontra.offPointer('down');
32 | kontra.offPointer('up');
--------------------------------------------------------------------------------
/test/typings/pool.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let pool: kontra.Pool = kontra.Pool({
4 | create() {
5 | return kontra.Sprite();
6 | }
7 | });
8 |
9 | let objs: object[] = pool.objects;
10 | let size: number = pool.size;
11 | let maxSize: number = pool.maxSize;
12 |
13 | let object: object = pool.get();
14 | let obj = pool.get({
15 | x: 1,
16 | y: 2
17 | });
18 | let aliveObjs: object[] = pool.getAliveObjects();
19 | pool.clear();
20 | pool.update();
21 | pool.update(1/60);
22 | pool.render();
23 |
24 | // options
25 | kontra.Pool({
26 | create() {
27 | return kontra.Sprite();
28 | },
29 | maxSize: 2
30 | });
31 |
32 | // custom create
33 | let customPool = kontra.Pool({
34 | create() {
35 | return {
36 | init() {},
37 | isAlive() {
38 | return true;
39 | },
40 | update() {},
41 | render() {}
42 | }
43 | }
44 | });
45 |
46 | // custom create with options
47 | let customPoolOpts = kontra.Pool({
48 | create() {
49 | return {
50 | init(properties) {},
51 | isAlive() {
52 | return true;
53 | },
54 | update(dt) {},
55 | render() {}
56 | }
57 | }
58 | });
59 |
60 | // kontra object create
61 | let spriteCreate = kontra.Pool({
62 | create: kontra.Sprite
63 | });
64 |
65 | // extends
66 | class CustomPool extends kontra.PoolClass {}
67 | let myPool = new CustomPool({
68 | create() {
69 | return kontra.Sprite();
70 | }
71 | });
72 | myPool.clear();
--------------------------------------------------------------------------------
/test/typings/quadtree.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let quadtree: kontra.Quadtree = kontra.Quadtree();
4 |
5 | let maxDepth: number = quadtree.maxDepth;
6 | let maxObjects: number = quadtree.maxObjects;
7 | let bounds: object = quadtree.bounds;
8 |
9 | quadtree.clear();
10 | let objs: object[] = quadtree.get({x: 10, y: 20, width: 100, height: 100});
11 | quadtree.get(kontra.Sprite());
12 |
13 | let obj = { x: 1, y: 2, width: 3, height: 4 };
14 |
15 | quadtree.add(obj);
16 | quadtree.add(obj, kontra.Sprite());
17 | quadtree.add([obj, kontra.Sprite()]);
18 |
19 | // options
20 | kontra.Quadtree({
21 | maxDepth: 1,
22 | maxObjects: 10,
23 | bounds: {
24 | x: 10,
25 | y: 20,
26 | width: 100,
27 | height: 100
28 | }
29 | });
30 |
31 | // extends
32 | class CustomQuadtree extends kontra.QuadtreeClass {}
33 | let myQuadtree = new CustomQuadtree();
34 | myQuadtree.get(kontra.Sprite());
--------------------------------------------------------------------------------
/test/typings/random.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let rand: number = kontra.rand();
4 | let randInt: number = kontra.randInt(10, 20);
5 |
6 | function randFn(): number {
7 | return 12;
8 | }
9 | let randIntWithFunc: number = kontra.randInt(10, 20, randFn);
10 |
11 | let seed: number = kontra.getSeed();
12 |
13 | kontra.seedRand('kontra');
14 | kontra.seedRand(Date.now());
15 | kontra.seedRand(123489719875);
16 | kontra.seedRand();
--------------------------------------------------------------------------------
/test/typings/scene.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let scene: kontra.Scene = kontra.Scene({
4 | id: 'game'
5 | });
6 |
7 | let id: string = scene.id;
8 | let name: string = scene.name;
9 | let hidden: boolean = scene.hidden;
10 | let context: CanvasRenderingContext2D = scene.context;
11 | let cullObjects: boolean = scene.cullObjects;
12 | let camera: kontra.GameObject = scene.camera;
13 | let node: HTMLElement = scene.node;
14 |
15 | scene.cullFunction();
16 | scene.show();
17 | scene.hide();
18 | scene.destroy();
19 | scene.update();
20 | scene.update(1/60);
21 | scene.render();
22 | scene.lookAt({x: 10, y: 20});
23 | scene.onShow();
24 | scene.onHide();
25 |
26 | scene.add({x: 10, y: 20});
27 |
28 | let sprite = kontra.Sprite();
29 | scene.add(sprite);
30 | scene.add({x: 10, y: 20}, sprite);
31 | scene.add([{x: 10, y: 20}, sprite]);
32 |
33 | scene.remove({x: 10, y: 20});
34 | scene.remove(sprite);
35 | scene.remove({x: 10, y: 20}, sprite);
36 | scene.remove([{x: 10, y: 20}, sprite]);
37 |
38 | // options
39 | kontra.Scene({
40 | id: 'options',
41 | name: 'Options Menu',
42 | objects: [kontra.GameObject()],
43 | onShow() {},
44 | onHide() {}
45 | });
46 |
47 | // custom props
48 | kontra.Scene({
49 | id: 'game'
50 | });
51 |
52 | // extends
53 | class CustomScene extends kontra.SceneClass {}
54 | let myScene = new CustomScene({
55 | id: 'game'
56 | });
57 | myScene.lookAt({x: 10, y: 20});
--------------------------------------------------------------------------------
/test/typings/sprite.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | // null
4 | let nullSprite: kontra.Sprite = kontra.Sprite();
5 |
6 | // inheritance
7 | nullSprite.x += 20;
8 | nullSprite.rotation = Math.PI;
9 | nullSprite.advance();
10 |
11 | // color
12 | let colorSprite = kontra.Sprite({
13 | color: 'red',
14 | width: 20,
15 | height: 40,
16 | x: 20,
17 | y: 20
18 | });
19 |
20 | // image
21 | let image = new Image();
22 | let imageSprite = kontra.Sprite({
23 | image
24 | });
25 |
26 | imageSprite.update();
27 | imageSprite.render();
28 |
29 | // animation
30 | let spriteSheet = kontra.SpriteSheet({
31 | image: kontra.imageAssets.character_walk_sheet,
32 | frameWidth: 72,
33 | frameHeight: 97,
34 | animations: {
35 | walk: {
36 | frames: '0..10',
37 | frameRate: 30
38 | }
39 | }
40 | });
41 |
42 | let animSprite = kontra.Sprite({
43 | width: 72 * 2,
44 | height: 97 * 2,
45 | anchor: {
46 | x: 0.5,
47 | y: 0.5,
48 | },
49 | x: 300,
50 | y: 200,
51 | animations: spriteSheet.animations
52 | });
53 |
54 | animSprite.playAnimation('walk');
55 | let anims = animSprite.animations;
56 | let currAnim = animSprite.currentAnimation;
57 |
58 | // extends
59 | class CustomSprite extends kontra.SpriteClass {
60 | constructor(properties?: object) {
61 | super(properties);
62 | }
63 | }
64 |
65 | let customSprite = new CustomSprite({
66 | x: 12,
67 | y: 10
68 | });
69 | customSprite.advance();
70 |
71 | // custom props
72 | let propSrpite = kontra.Sprite({
73 | custom: 'foo'
74 | });
75 | let prop = propSrpite.custom;
--------------------------------------------------------------------------------
/test/typings/spriteSheet.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let spriteSheet: kontra.SpriteSheet = kontra.SpriteSheet({
4 | image: new Image(),
5 | frameWidth: 32,
6 | frameHeight: 32
7 | });
8 |
9 | let width: number = spriteSheet.frame.width;
10 | let height: number = spriteSheet.frame.height;
11 | let margin: number = spriteSheet.frame.margin;
12 |
13 | spriteSheet.createAnimations({
14 | walk: {
15 | frames: 1,
16 | frameRate: 20
17 | }
18 | });
19 |
20 | // options
21 | kontra.SpriteSheet({
22 | image: new Image(),
23 | frameWidth: 32,
24 | frameHeight: 32,
25 | margin: 32,
26 | spacing: 2,
27 | animations: {
28 | walk: {
29 | frames: 1,
30 | frameRate: 20,
31 | loop: false
32 | }
33 | }
34 | });
35 |
36 | // extends
37 | class CustomSpriteSheet extends kontra.SpriteSheetClass {}
38 | let mySpriteSheet = new CustomSpriteSheet({
39 | image: new Image(),
40 | frameWidth: 32,
41 | frameHeight: 32
42 | });
43 | mySpriteSheet.createAnimations({
44 | walk: {
45 | frames: 1,
46 | frameRate: 20
47 | }
48 | });
--------------------------------------------------------------------------------
/test/typings/text.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let text: kontra.Text = kontra.Text({
4 | text: 'Hello World!',
5 | color: 'black',
6 | font: '32px Arial'
7 | });
8 |
9 | let str: string = text.text;
10 | let color: string = text.color;
11 | let font: string = text.font;
12 | let textAlign: string = text.textAlign;
13 | let width: number = text.width;
14 | let height: number = text.height;
15 | let lineHeight: number = text.lineHeight;
16 | let dir: string = text.dir;
17 |
18 | // inheritance
19 | text.x += 20;
20 | text.rotation = Math.PI;
21 | text.advance();
22 | text.render();
23 |
24 | // options
25 | kontra.Text({
26 | x: 10,
27 | y: 20,
28 | text: 'Hello World!',
29 | color: 'black',
30 | font: '32px Arial',
31 | textAlign: 'right',
32 | width: 200,
33 | lineHeight: 3,
34 | lineWidth: 3,
35 | strokeColor: 'white'
36 | });
37 |
38 | // extends
39 | class CustomText extends kontra.TextClass {}
40 | let myText = new CustomText({
41 | text: 'Hello World!',
42 | color: 'black',
43 | font: '32px Arial'
44 | });
45 | myText.advance();
--------------------------------------------------------------------------------
/test/typings/tileEngine.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let tileEngine: kontra.TileEngine = kontra.TileEngine({
4 | tilewidth: 64,
5 | tileheight: 64,
6 | width: 9,
7 | height: 9,
8 | tilesets: [{
9 | firstgid: 1,
10 | image: new Image()
11 | }],
12 | layers: [{
13 | name: 'ground',
14 | data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
15 | 0, 0, 6, 7, 7, 8, 0, 0, 0,
16 | 0, 6, 27, 24, 24, 25, 0, 0, 0,
17 | 0, 23, 24, 24, 24, 26, 8, 0, 0,
18 | 0, 23, 24, 24, 24, 24, 26, 8, 0,
19 | 0, 23, 24, 24, 24, 24, 24, 25, 0,
20 | 0, 40, 41, 41, 10, 24, 24, 25, 0,
21 | 0, 0, 0, 0, 40, 41, 41, 42, 0,
22 | 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
23 | }]
24 | });
25 |
26 | let width: number = tileEngine.width;
27 | let height: number = tileEngine.height;
28 | let tilewidth: number = tileEngine.tilewidth;
29 | let tileheight: number = tileEngine.tileheight;
30 | let layers: object[] = tileEngine.layers;
31 | let tilesets: object[] = tileEngine.tilesets;
32 | let context: CanvasRenderingContext2D = tileEngine.context;
33 | let mapwidth: number = tileEngine.mapwidth;
34 | let mapheight: number = tileEngine.mapheight;
35 | let sx: number = tileEngine.sx;
36 | let sy: number = tileEngine.sy;
37 |
38 | tileEngine.render();
39 | tileEngine.renderLayer('ground');
40 | let collides: boolean = tileEngine.layerCollidesWith('ground', {x: 10, y: 20, height: 100, width: 100});
41 | let collidesGameObject: boolean = tileEngine.layerCollidesWith('ground', kontra.GameObject());
42 | let tileX: number = tileEngine.tileAtLayer('ground', {x: 50, y: 50});
43 | let tileRow: number = tileEngine.tileAtLayer('ground', {row: 5, col: 5});
44 | tileEngine.setTileAtLayer('ground', {x: 50, y: 50}, 2);
45 | tileEngine.setTileAtLayer('ground', {row: 5, col: 5}, 2);
46 | tileEngine.setLayer('ground', [1,2,0,0,0,5,6]);
47 | tileEngine.add({});
48 | tileEngine.add({}, {});
49 | tileEngine.add([{}, {}]);
50 | tileEngine.add(kontra.GameObject());
51 | tileEngine.remove({});
52 | tileEngine.remove({}, {});
53 | tileEngine.remove([{}, {}]);
54 | tileEngine.remove(kontra.GameObject());
55 | const { x, y, row, col } = tileEngine.getPosition({x: 100, y: 200})
56 | tileEngine.getPosition({x: 100, y: 200} as PointerEvent)
57 | tileEngine.setTileAtLayer('ground', { x, y }, 5);
58 | tileEngine.setTileAtLayer('ground', { row, col }, 5);
59 |
60 | // options
61 | kontra.TileEngine({
62 | context: document.createElement('canvas').getContext('2d'),
63 | tilewidth: 64,
64 | tileheight: 64,
65 | width: 9,
66 | height: 9,
67 | tilesets: [{
68 | firstgid: 1,
69 | image: new Image()
70 | }],
71 | layers: [{
72 | name: 'ground',
73 | data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 | 0, 0, 6, 7, 7, 8, 0, 0, 0,
75 | 0, 6, 27, 24, 24, 25, 0, 0, 0,
76 | 0, 23, 24, 24, 24, 26, 8, 0, 0,
77 | 0, 23, 24, 24, 24, 24, 26, 8, 0,
78 | 0, 23, 24, 24, 24, 24, 24, 25, 0,
79 | 0, 40, 41, 41, 10, 24, 24, 25, 0,
80 | 0, 0, 0, 0, 40, 41, 41, 42, 0,
81 | 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
82 | }]
83 | });
--------------------------------------------------------------------------------
/test/typings/vector.ts:
--------------------------------------------------------------------------------
1 | import * as kontra from '../../kontra.js';
2 |
3 | let vector: kontra.Vector = kontra.Vector();
4 |
5 | let x: number = vector.x;
6 | let y: number = vector.y;
7 |
8 | vector.set({x: 10, y: 10});
9 | let newVec: kontra.Vector = vector.add({x: 1, y: 2});
10 | vector.add(newVec);
11 | vector.add({x: 10, y: 10});
12 | let subtract: kontra.Vector = vector.subtract(newVec);
13 | vector.subtract({x: 10, y: 10});
14 | let scale: kontra.Vector = vector.scale(5);
15 | let normalize: kontra.Vector = vector.normalize();
16 | let dot: number = vector.dot(newVec);
17 | vector.dot({x: 10, y: 10});
18 | let length: number = vector.length();
19 | let distance: number = vector.distance(newVec);
20 | vector.distance({x: 10, y: 10});
21 | let angle: number = vector.angle(newVec);
22 | vector.clamp(10, 20, 100, 100);
23 |
24 | // options
25 | kontra.Vector(10);
26 | kontra.Vector(10, 20);
27 | kontra.Vector({x: 10, y: 20});
28 |
29 | // extends
30 | class CustomVector extends kontra.VectorClass {}
31 | let myVec = new CustomVector();
32 | myVec.add(newVec);
--------------------------------------------------------------------------------
/test/unit/random.spec.js:
--------------------------------------------------------------------------------
1 | import * as random from '../../src/random.js';
2 |
3 | // --------------------------------------------------
4 | // random
5 | // --------------------------------------------------
6 | describe('random', () => {
7 | it('should export api', () => {
8 | expect(random.rand).to.be.an('function');
9 | expect(random.randInt).to.be.an('function');
10 | expect(random.getSeed).to.be.an('function');
11 | expect(random.seedRand).to.be.an('function');
12 | });
13 |
14 | // --------------------------------------------------
15 | // rand
16 | // --------------------------------------------------
17 | describe('rand', () => {
18 | it('should get random value between 0 and <1', () => {
19 | expect(random.rand()).to.be.within(0, 1);
20 | });
21 |
22 | it('should work if seed has not been seeded', () => {
23 | random._reset();
24 | expect(random.rand).to.not.throw();
25 | });
26 | });
27 |
28 | // --------------------------------------------------
29 | // randInt
30 | // --------------------------------------------------
31 | describe('randInt', () => {
32 | it('should get random integer between range', () => {
33 | expect(random.randInt(2, 10)).to.be.within(2, 10);
34 | });
35 | });
36 |
37 | // --------------------------------------------------
38 | // seedRand
39 | // --------------------------------------------------
40 | describe('seedRand', () => {
41 | function testSeededRandom() {
42 | expect(random.rand()).to.equal(0.26133555523119867);
43 |
44 | for (let i = 0; i < 20; i++) {
45 | random.rand();
46 | }
47 |
48 | expect(random.rand()).to.equal(0.08834491996094584);
49 | }
50 |
51 | // all seed values = 2859059487
52 | it('should seed with value', () => {
53 | random.seedRand(2859059487);
54 |
55 | testSeededRandom();
56 | });
57 |
58 | it('should seed with string', () => {
59 | random.seedRand('kontra');
60 |
61 | testSeededRandom();
62 | });
63 |
64 | it('should seed with time by default', () => {
65 | sinon.stub(Date, 'now').returns(2859059487);
66 | random.seedRand();
67 |
68 | testSeededRandom();
69 | });
70 | });
71 |
72 | // --------------------------------------------------
73 | // getSeed
74 | // --------------------------------------------------
75 | describe('getSeed', () => {
76 | it('should return the seed', () => {
77 | random.seedRand('kontra');
78 | expect(random.getSeed()).to.equal(2859059487);
79 | });
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/test/unit/utils.spec.js:
--------------------------------------------------------------------------------
1 | import * as utils from '../../src/utils.js';
2 | import { getCanvas } from '../../src/core.js';
3 |
4 | // --------------------------------------------------
5 | // utils
6 | // --------------------------------------------------
7 | describe('utils', () => {
8 | let canvas;
9 | beforeEach(() => {
10 | canvas = getCanvas();
11 | });
12 |
13 | // --------------------------------------------------
14 | // addToDom
15 | // --------------------------------------------------
16 | describe('addToDom', () => {
17 | it('adds the node as a sibling to the canvas', () => {
18 | const node = document.createElement('div');
19 | utils.addToDom(node, canvas);
20 |
21 | expect(canvas.nextSibling).to.equal(node);
22 | });
23 |
24 | it('adds the node to the body if canvas is disconnected', () => {
25 | const node = document.createElement('div');
26 | canvas.remove();
27 | utils.addToDom(node, canvas);
28 |
29 | expect(node.parentNode).to.equal(document.body);
30 | });
31 |
32 | it('adds the node to a container', () => {
33 | const div = document.createElement('div');
34 | div.append(canvas);
35 |
36 | const node = document.createElement('div');
37 | utils.addToDom(node, canvas);
38 |
39 | expect(div.contains(node)).to.be.true;
40 | });
41 |
42 | it('adds `data-kontra` attribute', () => {
43 | const node = document.createElement('div');
44 | utils.addToDom(node, canvas);
45 |
46 | expect(node.hasAttribute('data-kontra')).to.be.true;
47 | });
48 |
49 | it('adds the node as the next sibling to the last `data-kontra` node', () => {
50 | const container = document.createElement('div');
51 | container.append(canvas);
52 |
53 | const node1 = document.createElement('div');
54 | utils.addToDom(node1, canvas);
55 |
56 | const div = document.createElement('div');
57 | canvas.parentNode.append(div);
58 |
59 | const node2 = document.createElement('div');
60 | utils.addToDom(node2, canvas);
61 |
62 | expect(node1.nextSibling).to.equal(node2);
63 | expect(canvas.parentNode.lastChild).to.equal(div);
64 | });
65 |
66 | it('does not add the node as a child of an inner container with another `data-kontra` node', () => {
67 | const container = document.createElement('div');
68 | container.append(canvas);
69 |
70 | const innerContainer = document.createElement('div');
71 | container.append(innerContainer);
72 |
73 | const node1 = document.createElement('div');
74 | node1.setAttribute('data-kontra', '');
75 | innerContainer.appendChild(node1);
76 |
77 | const node2 = document.createElement('div');
78 | utils.addToDom(node2, canvas);
79 |
80 | expect(canvas.nextSibling).to.equal(node2);
81 | });
82 | });
83 |
84 | // --------------------------------------------------
85 | // removeFromArray
86 | // --------------------------------------------------
87 | describe('removeFromArray', () => {
88 | it('removes item from array', () => {
89 | const array = [1, 2, 3, 4];
90 | utils.removeFromArray(array, 3);
91 |
92 | expect(array).to.deep.equal([1, 2, 4]);
93 | });
94 |
95 | it('returns true when item is removed', () => {
96 | const array = [1, 2, 3, 4];
97 | const result = utils.removeFromArray(array, 3);
98 |
99 | expect(result).to.be.true;
100 | });
101 |
102 | it('does not remove item if not found', () => {
103 | const array = [1, 2, 3, 4];
104 | const result = utils.removeFromArray(array, 5);
105 |
106 | expect(array).to.deep.equal([1, 2, 3, 4]);
107 | expect(result).to.be.undefined;
108 | });
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simulate a keyboard event.
3 | * @param {string} type - Type of keyboard event.
4 | * @param {object} [config] - Additional settings for the event.
5 | * @param {HTMLElement} [node=window] - Node to dispatch the event on.
6 | */
7 | export function simulateEvent(type, config = {}, node = window) {
8 | let evt = new Event(type);
9 |
10 | for (let prop in config) {
11 | evt[prop] = config[prop];
12 | }
13 |
14 | if (config.async) {
15 | window.setTimeout(() => node.dispatchEvent(evt), 100);
16 | } else {
17 | node.dispatchEvent(evt);
18 | }
19 |
20 | return evt;
21 | }
22 |
23 | /**
24 | * Simulate a gamepad event.
25 | * @param {string} type - Type of gamepad event.
26 | * @param {object} gamepad - Gamepad object.
27 | * @param {number} gamepad.index - Index of the gamepad
28 | * @param {Object[]} [gamepad.buttons] - GamepadButtons and their state
29 | * @param {Number[]} [gamepad.axes] - Gamepad axes values
30 | *
31 | */
32 | export function simulateGamepadEvent(type, gamepad) {
33 | let evt = new GamepadEvent(type);
34 |
35 | // evt.gamepad is read-only so we need to override it
36 | Object.defineProperty(evt, 'gamepad', {
37 | value: {
38 | buttons: [],
39 | axes: [],
40 | ...gamepad
41 | }
42 | });
43 |
44 | window.dispatchEvent(evt);
45 |
46 | return evt;
47 | }
48 |
49 | export let getGamepadsStub = [];
50 | export function createGamepad(index = getGamepadsStub.length) {
51 | let gamepadObj = {
52 | index,
53 | connected: true,
54 | buttons: [],
55 | axes: [0, 0, 0, 0]
56 | };
57 | for (let i = 0; i < 16; i++) {
58 | gamepadObj.buttons[i] = { pressed: false };
59 | }
60 |
61 | simulateGamepadEvent('gamepadconnected', gamepadObj);
62 | getGamepadsStub[index] = gamepadObj;
63 | }
64 |
--------------------------------------------------------------------------------