├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── config
├── build-typings.js
├── jsdoc.conf.json
├── webpack.config.dev.js
└── webpack.config.js
├── dist
├── phaser-raycaster.js
├── phaser-raycaster.js.map
├── phaser-raycaster.min.js
└── phaser-raycaster.min.js.map
├── docs
├── Raycaster.Map.html
├── Raycaster.Ray.html
├── Raycaster.html
├── classes.list.html
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
├── img
│ ├── glyphicons-halflings-white.png
│ └── glyphicons-halflings.png
├── index.html
├── map_config.js.html
├── map_map-core.js.html
├── map_map-tilemap-methods.js.html
├── map_segmentsCount.js.html
├── quicksearch.html
├── ray_angle.js.html
├── ray_cast.js.html
├── ray_castCircle.js.html
├── ray_castCone.js.html
├── ray_cone.js.html
├── ray_config.js.html
├── ray_enablePhysics.js.html
├── ray_matter-physics-methods.js.html
├── ray_origin.js.html
├── ray_overlap.js.html
├── ray_range.js.html
├── ray_ray-core.js.html
├── ray_ray.js.html
├── raycaster-core.js.html
├── scripts
│ ├── docstrap.lib.js
│ ├── fulltext-search-ui.js
│ ├── fulltext-search.js
│ ├── lunr.min.js
│ ├── prettify
│ │ ├── Apache-License-2.0.txt
│ │ ├── jquery.min.js
│ │ ├── lang-css.js
│ │ └── prettify.js
│ ├── sunlight.js
│ └── toc.js
└── styles
│ ├── darkstrap.css
│ ├── prettify-tomorrow.css
│ ├── site.cerulean.css
│ ├── site.cosmo.css
│ ├── site.cyborg.css
│ ├── site.darkly.css
│ ├── site.darkstrap.css
│ ├── site.dibs-bootstrap.css
│ ├── site.flatly.css
│ ├── site.journal.css
│ ├── site.lumen.css
│ ├── site.paper.css
│ ├── site.readable.css
│ ├── site.sandstone.css
│ ├── site.simplex.css
│ ├── site.slate.css
│ ├── site.spacelab.css
│ ├── site.superhero.css
│ ├── site.united.css
│ ├── site.yeti.css
│ ├── sunlight.dark.css
│ └── sunlight.default.css
├── package.json
├── src
├── main.js
├── map
│ ├── boundingBox.js
│ ├── config.js
│ ├── destroy.js
│ ├── map-circle-methods.js
│ ├── map-container-methods.js
│ ├── map-core.js
│ ├── map-line-methods.js
│ ├── map-matterBody-methods.js
│ ├── map-polygon-methods.js
│ ├── map-rectangle-methods.js
│ ├── map-tilemap-methods.js
│ └── segmentsCount.js
├── ray
│ ├── angle.js
│ ├── cast.js
│ ├── castCircle.js
│ ├── castCone.js
│ ├── cone.js
│ ├── config.js
│ ├── debug.js
│ ├── destroy.js
│ ├── enablePhysics.js
│ ├── matter-physics-methods.js
│ ├── origin.js
│ ├── overlap.js
│ ├── range.js
│ ├── ray-core.js
│ ├── ray.js
│ ├── slice.js
│ └── stats.js
└── raycaster-core.js
└── types
└── types.d.ts
/.github/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 making 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 both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | 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 wiserim.msg.gmail.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 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Please do not update the README or CHANGELOG, we will do this when we merge your PR.
2 |
3 | This PR (delete as applicable)
4 |
5 | * Updates the Documentation
6 | * Adds a new feature
7 | * Fixes a bug
8 |
9 | Describe the changes below:
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 | package-lock.json
45 |
46 | # docs
47 | docs/node_modules/
48 | docs/jspm_packages/
49 | docs/package-lock.json
50 |
51 | # TypeScript v1 declaration files
52 | typings/
53 |
54 | # TypeScript cache
55 | *.tsbuildinfo
56 |
57 | # Optional npm cache directory
58 | .npm
59 |
60 | # Optional eslint cache
61 | .eslintcache
62 |
63 | # Microbundle cache
64 | .rpt2_cache/
65 | .rts2_cache_cjs/
66 | .rts2_cache_es/
67 | .rts2_cache_umd/
68 |
69 | # Optional REPL history
70 | .node_repl_history
71 |
72 | # Output of 'npm pack'
73 | *.tgz
74 |
75 | # Yarn Integrity file
76 | .yarn-integrity
77 |
78 | # dotenv environment variables file
79 | .env
80 | .env.test
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 |
85 | # Next.js build output
86 | .next
87 |
88 | # Nuxt.js build / generate output
89 | .nuxt
90 |
91 | # Gatsby files
92 | .cache/
93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
94 | # https://nextjs.org/blog/next-9-1#public-directory-support
95 | # public
96 |
97 | # vuepress build output
98 | .vuepress/dist
99 |
100 | # Serverless directories
101 | .serverless/
102 |
103 | # FuseBox cache
104 | .fusebox/
105 |
106 | # DynamoDB Local files
107 | .dynamodb/
108 |
109 | # TernJS port file
110 | .tern-port
111 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /docs
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Marcin Walczak
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/build-typings.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const { exec } = require('child_process');
4 |
5 | exec('node node_modules/jsdoc/jsdoc.js -t node_modules/tsd-jsdoc/dist -r src -d ./types', (err, stdout, stderr) => {
6 | if(err) {
7 | console.error(`exec error: ${err}`);
8 | return;
9 | }
10 |
11 | let file = fs.readFileSync(path.resolve(__dirname, '../types/types.d.ts'), 'utf8');
12 | file += `
13 | declare module 'phaser-raycaster' {
14 | export = PhaserRaycaster;
15 | }`;
16 |
17 | fs.writeFileSync(path.resolve(__dirname, '../types/types.d.ts'), file, 'utf8');
18 | console.log('types.d.ts file build successfully.');
19 | });
--------------------------------------------------------------------------------
/config/jsdoc.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [],
3 | "recurseDepth": 10,
4 | "source": {
5 | "include": [
6 | //raycaster
7 | "./src/raycaster-core.js",
8 | //ray
9 | "./src/ray/ray-core.js",
10 | "./src/ray/config.js",
11 | "./src/ray/cast.js",
12 | "./src/ray/castCircle.js",
13 | "./src/ray/castCone.js",
14 | "./src/ray/ray.js",
15 | "./src/ray/origin.js",
16 | "./src/ray/angle.js",
17 | "./src/ray/range.js",
18 | "./src/ray/cone.js",
19 | "./src/ray/overlap.js",
20 | "./src/ray/enablePhysics.js",
21 | "./src/ray/matter-physics-methods.js",
22 | //map
23 | "./src/map/map-core.js",
24 | "./src/map/config.js",
25 | "./src/map/segmentsCount.js",
26 | "./src/map/boundingBox.js",
27 | "./src/map/map-rectangle-methods.js",
28 | "./src/map/map-line-methods.js",
29 | "./src/map/map-circle-methods.js",
30 | "./src/map/map-polygon-methods.js",
31 | "./src/map/map-container-methods.js",
32 | "./src/map/map-tilemap-methods.js",
33 | "./src/map/map-matterBody-methods.js"
34 | ],
35 | "includePattern": ".+\\.js(doc|x)?$",
36 | "excludePattern": "(^|\\/|\\\\)_"
37 | },
38 | "sourceType": "module",
39 | "tags": {
40 | "allowUnknownTags": true,
41 | "dictionaries": ["jsdoc","closure"]
42 | },
43 | "opts": {
44 | "template": "./node_modules/ink-docstrap/template",
45 | "destination": "./docs"
46 | },
47 | "templates": {
48 | "cleverLinks": false,
49 | "monospaceLinks": false,
50 | "systemName": "Phaser-raycaster",
51 | "footer": "
');
152 |
153 | headings.each(function(i, heading) {
154 | var $h = $(heading);
155 |
156 | var anchor = $('
').attr('id', opts.anchorName(i, heading, opts.prefix) + ANCHOR_PREFIX).insertBefore($h);
157 |
158 | var span = $('
')
159 | .text(opts.headerText(i, heading, $h));
160 |
161 | //build TOC item
162 | var a = $('
')
163 | .append(span)
164 | .attr('href', '#' + opts.anchorName(i, heading, opts.prefix))
165 | .bind('click', function(e) {
166 | scrollTo(e);
167 | el.trigger('selected', $(this).attr('href'));
168 | });
169 |
170 | span.addClass(opts.itemClass(i, heading, $h, opts.prefix));
171 |
172 | tocs.push(a);
173 |
174 | ul.append(a);
175 | });
176 | el.html(ul);
177 |
178 | calcHadingOffsets();
179 | });
180 | };
181 |
182 |
183 | jQuery.fn.toc.defaults = {
184 | container: 'body',
185 | selectors: 'h1,h2,h3',
186 | smoothScrolling: true,
187 | prefix: 'toc',
188 | onHighlight: function() {},
189 | highlightOnScroll: true,
190 | navbarOffset: 0,
191 | anchorName: function(i, heading, prefix) {
192 | return prefix+i;
193 | },
194 | headerText: function(i, heading, $heading) {
195 | return $heading.text();
196 | },
197 | itemClass: function(i, heading, $heading, prefix) {
198 | return prefix + '-' + $heading[0].tagName.toLowerCase();
199 | }
200 |
201 | };
202 |
203 | })(jQuery);
204 |
--------------------------------------------------------------------------------
/docs/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Menlo, Monaco, Consolas, monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/styles/sunlight.dark.css:
--------------------------------------------------------------------------------
1 | /* global styles */
2 | .sunlight-container {
3 | clear: both !important;
4 | position: relative !important;
5 | margin: 10px 0 !important;
6 | }
7 | .sunlight-code-container {
8 | clear: both !important;
9 | position: relative !important;
10 | border: none;
11 | border-color: #626262 !important;
12 | background-color: #262626 !important;
13 | }
14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea {
15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important;
16 | font-size: 12px !important;
17 | line-height: 15px !important;
18 | }
19 | .sunlight-highlighted, .sunlight-container textarea {
20 | color: #FFFFFF !important;
21 | margin: 0 !important;
22 | }
23 | .sunlight-container textarea {
24 | padding-left: 0 !important;
25 | margin-left: 0 !important;
26 | margin-right: 0 !important;
27 | padding-right: 0 !important;
28 | }
29 | .sunlight-code-container > .sunlight-highlighted {
30 | white-space: pre;
31 | overflow-x: auto;
32 | overflow-y: hidden; /* ie requires this wtf? */
33 | }
34 | .sunlight-highlighted {
35 | z-index: 1;
36 | position: relative;
37 | }
38 | .sunlight-highlighted * {
39 | background: transparent;
40 | }
41 | .sunlight-line-number-margin {
42 | float: left !important;
43 | margin-right: 5px !important;
44 | margin-top: 0 !important;
45 | margin-bottom: 0 !important;
46 | padding: 0 !important;
47 | padding-right: 4px !important;
48 | padding-left: 4px !important;
49 | border-right: 1px solid #9A9A9A !important;
50 | background-color: #3E3E3E !important;
51 | color: #9A9A9A !important;
52 | text-align: right !important;
53 | position: relative;
54 | z-index: 3;
55 | }
56 | .sunlight-highlighted a, .sunlight-line-number-margin a {
57 | border: none !important;
58 | text-decoration: none !important;
59 | font-style: normal !important;
60 | padding: 0 !important;
61 | }
62 | .sunlight-line-number-margin a {
63 | color: inherit !important;
64 | }
65 | .sunlight-line-highlight-overlay {
66 | position: absolute;
67 | top: 0;
68 | left: 0;
69 | width: 100%;
70 | z-index: 0;
71 | }
72 | .sunlight-line-highlight-overlay div {
73 | height: 15px;
74 | width: 100%;
75 | }
76 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active {
77 | background-color: #4B4B4B;
78 | }
79 |
80 | /* menu */
81 | .sunlight-menu {
82 | background-color: #FFFFCC;
83 | color: #000000;
84 | }
85 | .sunlight-menu ul {
86 | margin: 0 !important;
87 | padding: 0 !important;
88 | list-style-type: none !important;
89 | }
90 | .sunlight-menu li {
91 | float: right !important;
92 | margin-left: 5px !important;
93 | }
94 | .sunlight-menu a, .sunlight-menu img {
95 | color: #000099 !important;
96 | text-decoration: none !important;
97 | border: none !important;
98 | }
99 |
100 |
101 |
102 |
103 | .sunlight-string,
104 | .sunlight-char,
105 | .sunlight-heredoc,
106 | .sunlight-heredocDeclaration,
107 | .sunlight-nowdoc,
108 | .sunlight-longString,
109 | .sunlight-rawString,
110 | .sunlight-binaryString,
111 | .sunlight-verbatimString,
112 | .sunlight-rawLongString,
113 | .sunlight-binaryLongString,
114 | .sunlight-diff .sunlight-added {
115 | color: #55EB54 !important;
116 | }
117 | .sunlight-operator,
118 | .sunlight-punctuation,
119 | .sunlight-delimiter {
120 | color: #B1EDEC !important;
121 | }
122 | .sunlight-ident,
123 | .sunlight-diff .sunlight-unchanged {
124 | color: #E0E0E0 !important;
125 | font-weight: bold !important;
126 | }
127 | .sunlight-comment,
128 | .sunlight-xmlDocCommentContent,
129 | .sunlight-nginx .sunlight-ssiCommand,
130 | .sunlight-sln .sunlight-formatDeclaration,
131 | .sunlight-diff .sunlight-mergeHeader,
132 | .sunlight-diff .sunlight-noNewLine {
133 | color: #787D31 !important;
134 | }
135 | .sunlight-number,
136 | .sunlight-cdata,
137 | .sunlight-guid,
138 | .sunlight-diff .sunlight-modified {
139 | color: #F7BA7E !important;
140 | font-weight: bold !important;
141 | }
142 | .sunlight-named-ident,
143 | .sunlight-xml .sunlight-attribute,
144 | .sunlight-constant,
145 | .sunlight-javascript .sunlight-globalVariable,
146 | .sunlight-globalObject,
147 | .sunlight-css .sunlight-id,
148 | .sunlight-python .sunlight-attribute,
149 | .sunlight-nginx .sunlight-context,
150 | .sunlight-httpd .sunlight-context,
151 | .sunlight-lisp .sunlight-declarationSpecifier,
152 | .sunlight-erlang .sunlight-userDefinedFunction,
153 | .sunlight-diff .sunlight-removed {
154 | color: #FBBDEE !important;
155 | font-weight: bold !important;
156 | }
157 | .sunlight-keyword,
158 | .sunlight-languageConstruct,
159 | .sunlight-specialOperator,
160 | .sunlight-xml .sunlight-tagName,
161 | .sunlight-xml .sunlight-operator,
162 | .sunlight-bash .sunlight-command,
163 | .sunlight-erlang .sunlight-moduleAttribute {
164 | color: #A3CCF7 !important;
165 | font-weight: bold !important;
166 | }
167 | .sunlight-shortOpenTag,
168 | .sunlight-openTag,
169 | .sunlight-closeTag,
170 | .sunlight-xmlOpenTag,
171 | .sunlight-xmlCloseTag,
172 | .sunlight-aspOpenTag,
173 | .sunlight-aspCloseTag,
174 | .sunlight-label,
175 | .sunlight-css .sunlight-importantFlag {
176 | background-color: #7373C1 !important;
177 | }
178 | .sunlight-content {
179 | color: #FFFFFF !important;
180 | font-weight: bold !important;
181 | }
182 | .sunlight-function,
183 | .sunlight-globalFunction,
184 | .sunlight-objective-c .sunlight-messageDestination,
185 | .sunlight-ruby .sunlight-specialFunction,
186 | .sunlight-6502asm .sunlight-illegalOpcode,
187 | .sunlight-powershell .sunlight-switch,
188 | .sunlight-lisp .sunlight-macro,
189 | .sunlight-lisp .sunlight-specialForm,
190 | .sunlight-lisp .sunlight-type,
191 | .sunlight-sln .sunlight-sectionName,
192 | .sunlight-diff .sunlight-header {
193 | color: #C8BBF1 !important;
194 | font-weight: bold !important;
195 | }
196 | .sunlight-variable,
197 | .sunlight-environmentVariable,
198 | .sunlight-specialVariable,
199 | .sunlight-objective-c .sunlight-messageArgumentName,
200 | .sunlight-lisp .sunlight-globalVariable,
201 | .sunlight-ruby .sunlight-globalVariable,
202 | .sunlight-ruby .sunlight-instanceVariable {
203 | color: #F5E5B0 !important;
204 | font-weight: bold !important;
205 | }
206 | .sunlight-regexLiteral,
207 | .sunlight-lisp .sunlight-operator,
208 | .sunlight-6502asm .sunlight-pseudoOp,
209 | .sunlight-erlang .sunlight-macro,
210 | .sunlight-diff .sunlight-rangeInfo {
211 | color: #E0F16A !important;
212 | }
213 | .sunlight-specialVariable {
214 | font-style: italic !important;
215 | font-weight: bold !important;
216 | }
217 | .sunlight-csharp .sunlight-pragma,
218 | .sunlight-preprocessorDirective,
219 | .sunlight-vb .sunlight-compilerDirective {
220 | color: #666363 !important;
221 | font-style: italic !important;
222 | }
223 | .sunlight-xmlDocCommentMeta,
224 | .sunlight-java .sunlight-annotation,
225 | .sunlight-scala .sunlight-annotation,
226 | .sunlight-docComment {
227 | color: #666363 !important;
228 | }
229 | .sunlight-quotedIdent,
230 | .sunlight-ruby .sunlight-subshellCommand,
231 | .sunlight-lisp .sunlight-keywordArgument,
232 | .sunlight-haskell .sunlight-infixOperator,
233 | .sunlight-erlang .sunlight-quotedAtom {
234 | color: #F8CA16 !important;
235 | }
236 |
237 |
238 |
239 |
240 | /* html/xml */
241 | .sunlight-xml .sunlight-tagName,
242 | .sunlight-xml .sunlight-operator,
243 | .sunlight-xml .sunlight-attribute {
244 | font-weight: normal !important;
245 | }
246 | .sunlight-doctype {
247 | color: #DEB9B2 !important;
248 | font-style: italic !important;
249 | }
250 | .sunlight-xml .sunlight-entity {
251 | background-color: #E6E585 !important;
252 | color: #000000 !important;
253 | }
254 |
255 | /* javascript */
256 | .sunlight-javascript .sunlight-reservedWord {
257 | font-style: italic !important;
258 | }
259 |
260 | /* css */
261 | .sunlight-css .sunlight-element {
262 | color: #E9EE97 !important;
263 | }
264 | .sunlight-css .sunlight-microsoftFilterPrefix {
265 | color: #C9FF9F !important;
266 | }
267 | .sunlight-css .sunlight-rule {
268 | color: #0099FF !important;
269 | }
270 | .sunlight-css .sunlight-class {
271 | color: #E78282 !important;
272 | }
273 | .sunlight-css .sunlight-pseudoClass, .sunlight-css .sunlight-pseudoElement {
274 | color: #73D693 !important;
275 | }
276 |
277 | /* bash */
278 | .sunlight-bash .sunlight-hashBang {
279 | color: #FFFF00 !important;
280 | }
281 |
282 | .sunlight-bash .sunlight-verbatimCommand {
283 | color: #BBA4EE !important;
284 | }
285 | .sunlight-bash .sunlight-variable,
286 | .sunlight-bash .sunlight-specialVariable {
287 | color: #ED8585 !important;
288 | }
289 |
290 | /* python */
291 | .sunlight-python .sunlight-specialMethod {
292 | font-weight: bold !important;
293 | color: #B0A3C2;
294 | }
295 |
296 | /* ruby */
297 | .sunlight-ruby .sunlight-symbol {
298 | font-weight: bold !important;
299 | color: #90EEA2 !important;
300 | }
301 |
302 | /* brainfuck */
303 | .sunlight-brainfuck {
304 | font-weight: bold !important;
305 | color: #000000 !important;
306 | }
307 | .sunlight-brainfuck .sunlight-increment {
308 | background-color: #FF9900 !important;
309 | }
310 | .sunlight-brainfuck .sunlight-decrement {
311 | background-color: #FF99FF !important;
312 | }
313 | .sunlight-brainfuck .sunlight-incrementPointer {
314 | background-color: #FFFF99 !important;
315 | }
316 | .sunlight-brainfuck .sunlight-decrementPointer {
317 | background-color: #66CCFF !important;
318 | }
319 | .sunlight-brainfuck .sunlight-read {
320 | background-color: #FFFFFF !important;
321 | }
322 | .sunlight-brainfuck .sunlight-write {
323 | background-color: #99FF99 !important;
324 | }
325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop {
326 | background-color: #FFFFFF !important;
327 | }
328 |
329 | /* 6502 asm */
330 | .sunlight-6502asm .sunlight-label {
331 | background: none !important;
332 | color: #FFFFFF !important;
333 | text-decoration: underline !important;
334 | }
335 |
336 | /* lisp */
337 | .sunlight-lisp .sunlight-macro {
338 | font-style: italic !important;
339 | }
340 |
341 | /* erlang */
342 | .sunlight-erlang .sunlight-atom {
343 | color: #FFFFFF !important;
344 | font-weight: bold !important;
345 | }
--------------------------------------------------------------------------------
/docs/styles/sunlight.default.css:
--------------------------------------------------------------------------------
1 | /* global styles */
2 | .sunlight-container {
3 | clear: both !important;
4 | position: relative !important;
5 | margin: 10px 0 !important;
6 | }
7 | .sunlight-code-container {
8 | clear: both !important;
9 | position: relative !important;
10 | border: none;
11 | border-color: #969696 !important;
12 | background-color: #FFFFFF !important;
13 | }
14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea {
15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important;
16 | font-size: 12px !important;
17 | line-height: 15px !important;
18 | }
19 | .sunlight-highlighted, .sunlight-container textarea {
20 | color: #000000 !important;
21 | margin: 0 !important;
22 | }
23 | .sunlight-container textarea {
24 | padding-left: 0 !important;
25 | margin-left: 0 !important;
26 | margin-right: 0 !important;
27 | padding-right: 0 !important;
28 | }
29 | .sunlight-code-container > .sunlight-highlighted {
30 | white-space: pre;
31 | overflow-x: auto;
32 | overflow-y: hidden; /* ie requires this wtf? */
33 | }
34 | .sunlight-highlighted {
35 | z-index: 1;
36 | position: relative;
37 | }
38 | .sunlight-highlighted * {
39 | background: transparent;
40 | }
41 | .sunlight-line-number-margin {
42 | float: left !important;
43 | margin-right: 5px !important;
44 | margin-top: 0 !important;
45 | margin-bottom: 0 !important;
46 | padding: 0 !important;
47 | padding-right: 4px !important;
48 | padding-left: 4px !important;
49 | border-right: 1px solid #CCCCCC !important;
50 | background-color: #EEEEEE !important;
51 | color: #848484 !important;
52 | text-align: right !important;
53 | position: relative;
54 | z-index: 3;
55 | }
56 | .sunlight-highlighted a, .sunlight-line-number-margin a {
57 | border: none !important;
58 | text-decoration: none !important;
59 | font-weight: normal !important;
60 | font-style: normal !important;
61 | padding: 0 !important;
62 | }
63 | .sunlight-line-number-margin a {
64 | color: inherit !important;
65 | }
66 | .sunlight-line-highlight-overlay {
67 | position: absolute;
68 | top: 0;
69 | left: 0;
70 | width: 100%;
71 | z-index: 0;
72 | }
73 | .sunlight-line-highlight-overlay div {
74 | height: 15px;
75 | width: 100%;
76 | }
77 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active {
78 | background-color: #E7FCFA;
79 | }
80 |
81 | /* menu */
82 | .sunlight-menu {
83 | background-color: #FFFFCC;
84 | color: #000000;
85 | }
86 | .sunlight-menu ul {
87 | margin: 0 !important;
88 | padding: 0 !important;
89 | list-style-type: none !important;
90 | }
91 | .sunlight-menu li {
92 | float: right !important;
93 | margin-left: 5px !important;
94 | }
95 | .sunlight-menu a, .sunlight-menu img {
96 | color: #000099 !important;
97 | text-decoration: none !important;
98 | border: none !important;
99 | }
100 |
101 |
102 |
103 |
104 | .sunlight-string,
105 | .sunlight-char,
106 | .sunlight-heredoc,
107 | .sunlight-heredocDeclaration,
108 | .sunlight-nowdoc,
109 | .sunlight-longString,
110 | .sunlight-rawString,
111 | .sunlight-binaryString,
112 | .sunlight-rawLongString,
113 | .sunlight-binaryLongString,
114 | .sunlight-verbatimString,
115 | .sunlight-diff .sunlight-removed {
116 | color: #990000 !important;
117 | }
118 |
119 | .sunlight-ident,
120 | .sunlight-operator,
121 | .sunlight-punctuation,
122 | .sunlight-delimiter,
123 | .sunlight-diff .sunlight-unchanged {
124 | color: #000000 !important;
125 | }
126 |
127 | .sunlight-comment,
128 | .sunlight-xmlDocCommentContent,
129 | .sunlight-nginx .sunlight-ssiCommand,
130 | .sunlight-sln .sunlight-formatDeclaration,
131 | .sunlight-diff .sunlight-added {
132 | color: #009900 !important;
133 | }
134 | .sunlight-number,
135 | .sunlight-guid,
136 | .sunlight-cdata {
137 | color: #CC6600 !important;
138 | }
139 |
140 | .sunlight-named-ident,
141 | .sunlight-constant,
142 | .sunlight-javascript .sunlight-globalVariable,
143 | .sunlight-globalObject,
144 | .sunlight-python .sunlight-attribute,
145 | .sunlight-nginx .sunlight-context,
146 | .sunlight-httpd .sunlight-context,
147 | .sunlight-haskell .sunlight-class,
148 | .sunlight-haskell .sunlight-type,
149 | .sunlight-lisp .sunlight-declarationSpecifier,
150 | .sunlight-erlang .sunlight-userDefinedFunction,
151 | .sunlight-diff .sunlight-header {
152 | color: #2B91AF !important;
153 | }
154 | .sunlight-keyword,
155 | .sunlight-languageConstruct,
156 | .sunlight-css
157 | .sunlight-element,
158 | .sunlight-bash .sunlight-command,
159 | .sunlight-specialOperator,
160 | .sunlight-erlang .sunlight-moduleAttribute,
161 | .sunlight-xml .sunlight-tagName,
162 | .sunlight-xml .sunlight-operator,
163 | .sunlight-diff .sunlight-modified {
164 | color: #0000FF !important;
165 | }
166 | .sunlight-shortOpenTag,
167 | .sunlight-openTag,
168 | .sunlight-closeTag,
169 | .sunlight-xmlOpenTag,
170 | .sunlight-xmlCloseTag,
171 | .sunlight-aspOpenTag,
172 | .sunlight-aspCloseTag,
173 | .sunlight-label,
174 | .sunlight-css .sunlight-importantFlag {
175 | background-color: #FFFF99 !important;
176 | color: #000000 !important;
177 | }
178 | .sunlight-function,
179 | .sunlight-globalFunction,
180 | .sunlight-ruby .sunlight-specialFunction,
181 | .sunlight-objective-c .sunlight-messageDestination,
182 | .sunlight-6502asm .sunlight-illegalOpcode,
183 | .sunlight-powershell .sunlight-switch,
184 | .sunlight-lisp .sunlight-macro,
185 | .sunlight-lisp .sunlight-specialForm,
186 | .sunlight-lisp .sunlight-type,
187 | .sunlight-sln .sunlight-sectionName,
188 | .sunlight-diff .sunlight-rangeInfo {
189 | color: #B069AF !important;
190 | }
191 |
192 | .sunlight-variable,
193 | .sunlight-specialVariable,
194 | .sunlight-environmentVariable,
195 | .sunlight-objective-c .sunlight-messageArgumentName,
196 | .sunlight-lisp .sunlight-globalVariable,
197 | .sunlight-ruby .sunlight-globalVariable,
198 | .sunlight-ruby .sunlight-instanceVariable,
199 | .sunlight-sln .sunlight-operator {
200 | color: #325484 !important;
201 | }
202 | .sunlight-regexLiteral,
203 | .sunlight-lisp .sunlight-operator,
204 | .sunlight-6502asm .sunlight-pseudoOp,
205 | .sunlight-erlang .sunlight-macro {
206 | color: #FF00B2 !important;
207 | }
208 | .sunlight-specialVariable {
209 | font-style: italic !important;
210 | font-weight: bold !important;
211 | }
212 | .sunlight-csharp .sunlight-pragma,
213 | .sunlight-preprocessorDirective,
214 | .sunlight-vb .sunlight-compilerDirective,
215 | .sunlight-diff .sunlight-mergeHeader,
216 | .sunlight-diff .sunlight-noNewLine {
217 | color: #999999 !important;
218 | font-style: italic !important;
219 | }
220 | .sunlight-xmlDocCommentMeta,
221 | .sunlight-java .sunlight-annotation,
222 | .sunlight-scala .sunlight-annotation,
223 | .sunlight-docComment {
224 | color: #808080 !important;
225 | }
226 | .sunlight-quotedIdent,
227 | .sunlight-ruby .sunlight-subshellCommand,
228 | .sunlight-lisp .sunlight-keywordArgument,
229 | .sunlight-haskell .sunlight-infixOperator,
230 | .sunlight-erlang .sunlight-quotedAtom {
231 | color: #999900 !important;
232 | }
233 |
234 |
235 |
236 | /* xml */
237 | .sunlight-xml .sunlight-string {
238 | color: #990099 !important;
239 | }
240 | .sunlight-xml .sunlight-attribute {
241 | color: #FF0000 !important;
242 | }
243 | .sunlight-xml .sunlight-entity {
244 | background-color: #EEEEEE !important;
245 | color: #000000 !important;
246 | border: 1px solid #000000 !important;
247 | }
248 | .sunlight-xml .sunlight-doctype {
249 | color: #2B91AF !important;
250 | }
251 |
252 | /* javascript */
253 | .sunlight-javascript .sunlight-reservedWord {
254 | font-style: italic !important;
255 | }
256 |
257 | /* css */
258 | .sunlight-css .sunlight-microsoftFilterPrefix {
259 | color: #FF00FF !important;
260 | }
261 | .sunlight-css .sunlight-rule {
262 | color: #0099FF !important;
263 | }
264 | .sunlight-css .sunlight-keyword {
265 | color: #4E65B8 !important;
266 | }
267 | .sunlight-css .sunlight-class {
268 | color: #FF0000 !important;
269 | }
270 | .sunlight-css .sunlight-id {
271 | color: #8A8E13 !important;
272 | }
273 | .sunlight-css .sunlight-pseudoClass,
274 | .sunlight-css .sunlight-pseudoElement {
275 | color: #368B87 !important;
276 | }
277 |
278 | /* bash */
279 | .sunlight-bash .sunlight-hashBang {
280 | color: #3D97F5 !important;
281 | }
282 | .sunlight-bash .sunlight-verbatimCommand {
283 | color: #999900 !important;
284 | }
285 | .sunlight-bash .sunlight-variable,
286 | .sunlight-bash .sunlight-specialVariable {
287 | color: #FF0000 !important;
288 | }
289 |
290 | /* python */
291 | .sunlight-python .sunlight-specialMethod {
292 | font-weight: bold !important;
293 | color: #A07DD3;
294 | }
295 |
296 | /* ruby */
297 | .sunlight-ruby .sunlight-symbol {
298 | font-weight: bold !important;
299 | color: #ED7272 !important;
300 | }
301 |
302 | /* brainfuck */
303 | .sunlight-brainfuck {
304 | font-weight: bold !important;
305 | color: #000000 !important;
306 | }
307 | .sunlight-brainfuck .sunlight-increment {
308 | background-color: #FF9900 !important;
309 | }
310 | .sunlight-brainfuck .sunlight-decrement {
311 | background-color: #FF99FF !important;
312 | }
313 | .sunlight-brainfuck .sunlight-incrementPointer {
314 | background-color: #FFFF99 !important;
315 | }
316 | .sunlight-brainfuck .sunlight-decrementPointer {
317 | background-color: #66CCFF !important;
318 | }
319 | .sunlight-brainfuck .sunlight-read {
320 | background-color: #FFFFFF !important;
321 | }
322 | .sunlight-brainfuck .sunlight-write {
323 | background-color: #99FF99 !important;
324 | }
325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop {
326 | background-color: #FFFFFF !important;
327 | }
328 |
329 | /* 6502 asm */
330 | .sunlight-6502asm .sunlight-label {
331 | font-weight: bold !important;
332 | color: #000000 !important;
333 | background: none !important;
334 | }
335 |
336 | /* lisp */
337 | .sunlight-lisp .sunlight-macro {
338 | font-style: italic !important;
339 | }
340 |
341 | /* erlang */
342 | .sunlight-erlang .sunlight-atom {
343 | font-weight: bold !important;
344 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser-raycaster",
3 | "version": "0.10.11",
4 | "description": "Raycasting plugin for Phaser 3.",
5 | "main": "dist/phaser-raycaster.min.js",
6 | "types": "types/types.d.ts",
7 | "scripts": {
8 | "dev": "webpack --mode development --config config/webpack.config.dev.js --watch",
9 | "build": "webpack --mode development --config config/webpack.config.dev.js && webpack --mode production --config config/webpack.config.js",
10 | "build-typings": "node config/build-typings.js",
11 | "build-docs": "node node_modules/jsdoc/jsdoc.js -c config/jsdoc.conf.json -R README.md"
12 | },
13 | "browserslist": [
14 | "last 1 version",
15 | "> 1%"
16 | ],
17 | "keywords": [
18 | "phaser",
19 | "phaser3",
20 | "phaser-plugin",
21 | "plugin",
22 | "raycast"
23 | ],
24 | "author": "Marcin Walczak",
25 | "license": "ISC",
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/wiserim/phaser-raycaster.git"
29 | },
30 | "devDependencies": {
31 | "@babel/core": "^7.26.0",
32 | "@babel/preset-env": "^7.26.0",
33 | "@babel/register": "^7.25.9",
34 | "babel-loader": "^9.2.1",
35 | "ink-docstrap": "^1.3.2",
36 | "jsdoc": "^3.6.11",
37 | "tsd-jsdoc": "^2.5.0",
38 | "webpack": "^5.96.1",
39 | "webpack-cli": "^5.1.4"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Marcin Walczak
3 | * @copyright 2023 Marcin Walczak
4 | * @license {@link https://github.com/wiserim/phaser-raycaster/blob/master/LICENSE|MIT License}
5 | */
6 |
7 | /**
8 | * Point object
9 | * @typedef {Object} Point
10 | * @property {number} x
11 | * @property {number} y
12 | */
13 |
14 | /**
15 | * @classdesc
16 | *
17 | * Raycaster plugin class.
18 | *
19 | * @namespace PhaserRaycaster
20 | * @class PhaserRaycaster
21 | * @extends Phaser.Plugins.ScenePlugin
22 | * @constructor
23 | * @since 0.6.0
24 | *
25 | * @param {Phaser.Scene} scene
26 | * @param {Phaser.Plugins.PluginManager} pluginManager
27 | */
28 |
29 | class PhaserRaycaster extends Phaser.Plugins.ScenePlugin {
30 | constructor(scene, pluginManager) {
31 | super(scene, pluginManager);
32 |
33 | this._Raycaster = require('./raycaster-core.js').Raycaster;
34 | }
35 |
36 | /**
37 | * Create Raycaster object.
38 | *
39 | * @method PhaserRaycaster#createRaycaster
40 | * @memberof PhaserRaycaster
41 | * @instance
42 | * @since 0.6.0
43 | *
44 | * @param {object} [options] - Raycaster's congfiguration options. May include:
45 | * @param {number} [options.mapSegmentCount = 0] - Number of segments of circle maps. If set to 0, map will be teste
46 | * @param {(object|object[])} [options.objects] - Game object or array of game objects to map.
47 | * @param {Phaser.Geom.Rectangle} [options.boundingBox] - Raycaster's bounding box. If not passed, {@link Raycaster Raycaster} will set it's bounding box based on Arcade Physics / Matter physics world bounds.
48 | * @param {boolean} [options.autoUpdate = true] - If set true, automatically update dynamic maps on scene update event.
49 | * @param {boolean|object} [options.debug] - Enable debug mode or configure it {@link Raycaster#debugOptions debugOptions}.
50 | *
51 | * @return {Raycaster} {@link Raycaster Raycaster} instance
52 | */
53 | createRaycaster(options = {}) {
54 | options.scene = this.scene;
55 | return new this._Raycaster(options);
56 | }
57 | }
58 |
59 | //Make sure you export the plugin for webpack to expose
60 | module.exports = PhaserRaycaster;
--------------------------------------------------------------------------------
/src/map/boundingBox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get mapped object's bounding box.
3 | *
4 | * @method Raycaster.Map#matterBody.getBoundingBox
5 | * @memberof Raycaster.Map
6 | * @instance
7 | * @private
8 | * @since 0.9.0
9 | *
10 | * @return {Phaser.Geom.Rectangle} - Mapped object's bounding box.
11 | */
12 | export function getBoundingBox() {
13 | return this.object.getBounds();
14 | }
15 |
--------------------------------------------------------------------------------
/src/map/config.js:
--------------------------------------------------------------------------------
1 | let rectangle = require('./map-rectangle-methods.js');
2 | let line = require('./map-line-methods.js');
3 | let polygon = require('./map-polygon-methods.js');
4 | let arc = require('./map-circle-methods.js');
5 | let container = require('./map-container-methods.js');
6 | let tilemap = require('./map-tilemap-methods.js');
7 | let matterBody = require('./map-matterBody-methods.js');
8 | let segmentCount = require('./segmentsCount.js');
9 | let boundingBox = require('./boundingBox.js');
10 |
11 | /**
12 | * Configure map.
13 | *
14 | * @method Raycaster.Map#config
15 | * @memberof Raycaster.Map
16 | * @instance
17 | * @since 0.6.0
18 | *
19 | * @param {object} [options] - Map's congfiguration options. May include:
20 | * @param {object} options.object - Game object to map
21 | * @param {string} [options.type] - Map type. If not defined, it will be determined based on object.
22 | * @param {boolean} [options.dynamic = false] - If set true, map will be dynamic (updated on scene update event).
23 | * @param {boolean} [options.active = true] - If set true, map will be active (will provide points, segments and will be updated).
24 | * @param {number} [options.segmentCount] - Circle map's segment count. If set to 0, map won't be generating segments and relay only on tangent points calculated for currently testing ray.
25 | * @param {object} [options.mapChild] - Container's child. If set, only set child will be mapped.
26 | * @param {boolean} [options.forceConvex] - If set true, matter body map will use convex body (hull) for non-covex bodies.
27 | * @param {boolean} [options.forceVerticesMapping] - If set true, matter body map will use only vertices for mapping circle bodies.
28 | *
29 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
30 | */
31 | export function config(options) {
32 | this.object = options.object;
33 | //object type
34 | if(options.type === undefined)
35 | options.type = options.object.type;
36 | if(options.type === 'body' || options.type === 'composite')
37 | options.type = 'MatterBody';
38 | this.type = options.type;
39 |
40 | switch(options.type) {
41 | case 'Polygon':
42 | this.getPoints = polygon.getPoints;
43 | this.getSegments = polygon.getSegments;
44 | this.getBoundingBox = boundingBox.getBoundingBox;
45 | this.updateMap = polygon.updateMap;
46 | break;
47 | case 'Arc':
48 | //circle segments count
49 | this.segmentCount = (options.segmentCount) ? options.segmentCount : 0;
50 | this.circle = (options.segmentCount) ? false : true;
51 | this.getPoints = arc.getPoints;
52 | this.getSegments = arc.getSegments;
53 | this.getBoundingBox = boundingBox.getBoundingBox;
54 | this.updateMap = arc.updateMap;
55 | this.setSegmentCount = segmentCount.setSegmentCount;
56 | break;
57 | case 'Line':
58 | this.getPoints = line.getPoints;
59 | this.getSegments = line.getSegments;
60 | this.getBoundingBox = boundingBox.getBoundingBox;
61 | this.updateMap = line.updateMap;
62 | break;
63 | case 'Container':
64 | //container's child
65 | this.mapChild = (options.mapChild) ? options.mapChild : null;
66 | //circle segments count
67 | this.segmentCount = (options.segmentCount) ? options.segmentCount : 0;
68 | //transformed container's circle children
69 | this._circles = [];
70 | this.getPoints = container.getPoints;
71 | this.getSegments = container.getSegments;
72 | this.getBoundingBox = boundingBox.getBoundingBox;
73 | this.updateMap = container.updateMap;
74 | this._updateChildMap = container._updateChildMap;
75 | this.setSegmentCount = segmentCount.setSegmentCount;
76 | break;
77 | case 'StaticTilemapLayer':
78 | //ray colliding tiles
79 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : [];
80 | this.getPoints = tilemap.getPoints;
81 | this.getSegments = tilemap.getSegments;
82 | this.getBoundingBox = boundingBox.getBoundingBox;
83 | this.updateMap = tilemap.updateMap;
84 | this.setCollisionTiles = tilemap.setCollisionTiles;
85 | //reset tilemap origin
86 | this.object.setOrigin(0,0);
87 | break;
88 | case 'DynamicTilemapLayer':
89 | //ray colliding tiles
90 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : [];
91 | this.getPoints = tilemap.getPoints;
92 | this.getSegments = tilemap.getSegments;
93 | this.getBoundingBox = boundingBox.getBoundingBox;
94 | this.updateMap = tilemap.updateMap;
95 | this.setCollisionTiles = tilemap.setCollisionTiles;
96 | //reset tilemap origin
97 | this.object.setOrigin(0,0);
98 | break;
99 | case 'TilemapLayer':
100 | //ray colliding tiles
101 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : [];
102 | this.getPoints = tilemap.getPoints;
103 | this.getSegments = tilemap.getSegments;
104 | this.getBoundingBox = boundingBox.getBoundingBox;
105 | this.updateMap = tilemap.updateMap;
106 | this.setCollisionTiles = tilemap.setCollisionTiles;
107 | //reset tilemap origin
108 | this.object.setOrigin(0,0);
109 | break;
110 | case 'MatterBody':
111 | //force convex body (hull) mapping
112 | this.forceConvex = (options.forceConvex) ? true : false;
113 | //force mapping by vertices
114 | this.forceVerticesMapping = (options.forceVerticesMapping) ? true : false;
115 | this.circle = false;
116 | this.getPoints = matterBody.getPoints;
117 | this.getSegments = matterBody.getSegments;
118 | this.getBoundingBox = matterBody.getBoundingBox;
119 | this.updateMap = matterBody.updateMap;
120 | break;
121 | default:
122 | this.getPoints = rectangle.getPoints;
123 | this.getSegments = rectangle.getSegments;
124 | this.getBoundingBox = boundingBox.getBoundingBox;
125 | this.updateMap = rectangle.updateMap;
126 | }
127 |
128 | //if object is not supported
129 | if(this.type != 'MatterBody' && typeof this.object.getBounds !== 'function') {
130 | this.notSupported = true;
131 | }
132 |
133 | //dynamic map
134 | this.dynamic = (options.dynamic == true) ? true : false;
135 |
136 | //enable/disable map
137 | this.active = (options.active !== undefined) ? options.active : true;
138 |
139 | return this;
140 | }
141 |
--------------------------------------------------------------------------------
/src/map/destroy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Destroy object
3 | *
4 | * @method Raycaster.Map#destroy
5 | * @memberof Raycaster.Map
6 | * @instance
7 | * @since 0.10.3
8 | */
9 | export function destroy() {
10 | //destroy reference to map object in mapped object
11 | if(this.object.type === 'body' || this.object.type === 'composite') {
12 | delete this.object.raycasterMap;
13 | }
14 | else if(this.object.data) {
15 | this.object.data.remove('raycasterMap');
16 | }
17 |
18 | for(let key in this) {
19 | delete this[key];
20 | }
21 | }
--------------------------------------------------------------------------------
/src/map/map-circle-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for circles*/
2 | /**
3 | * Get array of mapped circle's vertices used as rays targets.
4 | * If {@link Raycaster.Map#segmentCount Raycaster.Map#segmentCount} is set to 0, it'll calculatoe tangent points for passed ray.
5 | *
6 | * @method Raycaster.Map#arc.getPoints
7 | * @memberof Raycaster.Map
8 | * @instance
9 | * @private
10 | * @since 0.6.0
11 | *
12 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
13 | *
14 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
15 | */
16 | export function getPoints(ray = false) {
17 | if(!this.active)
18 | return [];
19 |
20 | if(this._points.length > 0)
21 | return this._points;
22 |
23 | let points = [];
24 | let offset = new Phaser.Geom.Point();
25 | offset.x = this.object.x - this.object.displayWidth * (this.object.originX - 0.5);
26 | offset.y = this.object.y - this.object.displayHeight * (this.object.originY - 0.5);
27 |
28 | //calculate tangent rays
29 | if(ray) {
30 | let rayA = new Phaser.Geom.Line();
31 | let rayB = new Phaser.Geom.Line();
32 | let c;
33 |
34 | let rotation = this.object.rotation;
35 |
36 | if(rotation !== 0) {
37 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, offset.x, offset.y);
38 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector));
39 | let cB = vector.getPointB();
40 | c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, cB.x, cB.y);
41 | }
42 | else {
43 | c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, offset.x, offset.y);
44 | }
45 |
46 | let rayLength = Math.sqrt(Math.pow(Phaser.Geom.Line.Length(c), 2) - Math.pow(this.object.radius * this.object.scaleX, 2));
47 |
48 | //ray angle
49 | let angle = Phaser.Geom.Line.Angle(c);
50 | let dAngle = Math.asin((this.object.radius * this.object.scaleX) / Phaser.Geom.Line.Length(c));
51 | Phaser.Geom.Line.SetToAngle(rayA, ray.origin.x, ray.origin.y, angle - dAngle, rayLength);
52 | Phaser.Geom.Line.SetToAngle(rayB, ray.origin.x, ray.origin.y, angle + dAngle, rayLength);
53 |
54 | //add tangent points
55 | points.push(rayA.getPointB());
56 | points.push(rayB.getPointB());
57 | //assign neighbours
58 | points[0].neighbours = [points[1]];
59 | points[1].neighbours = [points[0]];
60 | }
61 |
62 | return points;
63 | };
64 |
65 | /**
66 | * Get array of mapped circle's segments used to test object's intersection with ray.
67 | * If {@link Raycaster.Map#segmentCount Raycaster.Map#segmentCount} is set to 0, it'll return empty array.
68 | *
69 | * @method Raycaster.Map#arc.getSegments
70 | * @memberof Raycaster.Map
71 | * @instance
72 | * @private
73 | * @since 0.6.0
74 | *
75 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
76 | */
77 | export function getSegments() {
78 | if(!this.active)
79 | return [];
80 | return this._segments;
81 | };
82 |
83 | /**
84 | * Update circles's map of points and segments.
85 | *
86 | * @method Raycaster.Map#arc.updateMap
87 | * @memberof Raycaster.Map
88 | * @instance
89 | * @private
90 | * @since 0.6.0
91 | *
92 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
93 | */
94 | export function updateMap() {
95 | if(!this.active)
96 | return this;
97 |
98 | if(!this.segmentCount) {
99 | this._points = [];
100 | this._segments = [];
101 | return this;
102 | }
103 |
104 | //calculate offset based on object position and origin point
105 | let offset = new Phaser.Geom.Point();
106 | offset.x = this.object.x - this.object.displayWidth * this.object.originX + this.object.radius * this.object.scaleX;
107 | offset.y = this.object.y - this.object.displayHeight * this.object.originY + this.object.radius * this.object.scaleY;
108 |
109 | //get points surrounding circle
110 | let points = this.object.geom.getPoints(this.segmentCount);
111 | let segments = []
112 |
113 | //set points
114 | //calculate positions after object's rotation
115 | let rotation = this.object.rotation;
116 | if(rotation !== 0) {
117 | let newPoints = [];
118 | for(let point of points) {
119 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, this.object.x + (point.x + this.object.radius) * this.object.scaleX, this.object.y + (point.y + this.object.radius) * this.object.scaleY);
120 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector));
121 | newPoints.push(vector.getPointB());
122 | }
123 | points = newPoints;
124 | }
125 | //if rotation === 0
126 | else {
127 | for(let point of points) {
128 | point.x = point.x * this.object.scaleX + offset.x;
129 | point.y = point.y * this.object.scaleY + offset.y;
130 | }
131 | }
132 |
133 | //set segments
134 | for(let i = 0, length = points.length; i < length; i++) {
135 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0],
136 | nextPoint = i < length - 1 ? points[i + 1] : points[0];
137 |
138 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y));
139 |
140 | points[i].neighbours = [
141 | prevPoint,
142 | nextPoint
143 | ];
144 | }
145 |
146 | this._points = points;
147 | this._segments = segments;
148 | return this;
149 | };
150 |
--------------------------------------------------------------------------------
/src/map/map-core.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @classdesc
3 | *
4 | * Map class responsible for mapping game objects.
5 | *
6 | * @namespace Raycaster.Map
7 | * @class Raycaster.Map
8 | * @constructor
9 | * @since 0.6.0
10 | *
11 | * @param {object} options - Map specific configuration settings.
12 | * @param {Raycaster} [raycaster] - Parent raycaster object.
13 | */
14 | export function Map(options, raycaster) {
15 | /**
16 | * Reference to parent Raycaster object.
17 | *
18 | * @name Raycaster.Map#_raycaster
19 | * @type {Raycaster}
20 | * @private
21 | * @since 0.9.0
22 | */
23 | this._raycaster = raycaster ? raycaster : false;
24 | /**
25 | * Mapped object's type
26 | *
27 | * @name Raycaster.Map#type
28 | * @type {string}
29 | * @readonly
30 | * @since 0.6.0
31 | */
32 | this.type;
33 | /**
34 | * If set true, map will be tested by ray. Otherwise it will be ignored.
35 | *
36 | * @name Raycaster.Map#active
37 | * @type {boolean}
38 | * @default true
39 | * @since 0.7.2
40 | */
41 | this.active;
42 | /**
43 | * If set true, map will be automatically updated on scene update event.
44 | *
45 | * @name Raycaster.Map_dynamic
46 | * @type {boolean}
47 | * @default false
48 | * @since 0.6.0
49 | */
50 | this._dynamic = false;
51 | /**
52 | * If set true, map will be treated by ray as circle. Set automaticalyy on map update.
53 | *
54 | * @name Raycaster.Map#circle
55 | * @type {boolean}
56 | * @default false
57 | * @since 0.9.0
58 | */
59 | this.circle = false;
60 | /**
61 | * Reference to mapped object.
62 | *
63 | * @name Raycaster.Map#object
64 | * @type {object}
65 | * @readonly
66 | * @since 0.6.0
67 | */
68 | this.object;
69 | /**
70 | * Array of mapped object's vertices used as rays targets.
71 | *
72 | * @name Raycaster.Map#_points
73 | * @type {array}
74 | * @private
75 | * @since 0.6.0
76 | */
77 | this._points = [];
78 | /**
79 | * Array of mapped object's segments used to test object's intersection with ray.
80 | *
81 | * @name Raycaster.Map#_segments
82 | * @type {array}
83 | * @private
84 | * @since 0.6.0
85 | */
86 | this._segments = [];
87 | /**
88 | * Get array of mapped object's vertices used as rays targets.
89 | *
90 | * @method Raycaster.Map#getPoints
91 | * @memberof Raycaster.Map
92 | * @instance
93 | * @since 0.6.0
94 | *
95 | * @param {Raycaster.Ray} [ray] - {@link Raycaster.Ray Raycaster.Ray} object used in some some types of maps.
96 | *
97 | * @return {Phaser.Geom.Point[]} Array of mapped object's vertices.
98 | */
99 | this.getPoints;
100 | /**
101 | * Get array of mapped object's segments used to test object's intersection with ray.
102 | *
103 | * @method Raycaster.Map#getSegments
104 | * @memberof Raycaster.Map
105 | * @instance
106 | * @since 0.6.0
107 | *
108 | * @param {Raycaster.Ray} [ray] - {@link Raycaster.Ray Raycaster.Ray} object used in some some types of maps.
109 | *
110 | * @return {Phaser.Geom.Line[]} Array of mapped object's segments.
111 | */
112 | this.getSegments;
113 | /**
114 | * Get mapped object's bounding box.
115 | *
116 | * @method Raycaster.Map#getBoundingBox
117 | * @memberof Raycaster.Map
118 | * @instance
119 | * @since 0.9.0
120 | *
121 | * @return {Phaser.Geom.Rectangle} Mapped object's bounding box.
122 | */
123 | this.getBoundingBox;
124 | /**
125 | * Update object's map of points and segments.
126 | *
127 | * @method Raycaster.Map#updateMap
128 | * @memberof Raycaster.Map
129 | * @instance
130 | * @since 0.6.0
131 | *
132 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
133 | */
134 | this.updateMap;
135 |
136 | this.config(options);
137 | if(!this.notSupported)
138 | this.updateMap();
139 |
140 | return this;
141 | };
142 |
143 | Map.prototype = {
144 | config: require('./config.js').config,
145 | destroy: require('./destroy.js').destroy,
146 | get dynamic() {
147 | return this._dynamic;
148 | },
149 | set dynamic(dynamic) {
150 | if(this._dynamic == dynamic)
151 | return this;
152 |
153 | if(dynamic) {
154 | this._dynamic = true;
155 |
156 | //add object to raycaster's dynamic objects list
157 | if(this._raycaster) {
158 | this._raycaster.dynamicMappedObjects.push(this.object);
159 |
160 | this._raycaster._stats.mappedObjects.dynamic = this._raycaster.dynamicMappedObjects.length;
161 | this._raycaster._stats.mappedObjects.static = this._raycaster._stats.mappedObjects.total - this._raycaster._stats.mappedObjects.dynamic;
162 | }
163 | }
164 | else {
165 | this._dynamic = false;
166 |
167 | //remove object from reycasters' dynamic objects list
168 | if(this._raycaster) {
169 | let index = this._raycaster.dynamicMappedObjects.indexOf(this.object);
170 | if(index >= 0)
171 | this._raycaster.dynamicMappedObjects.splice(index, 1);
172 |
173 | this._raycaster._stats.mappedObjects.dynamic = this._raycaster.dynamicMappedObjects.length;
174 | this._raycaster._stats.mappedObjects.static = this._raycaster._stats.mappedObjects.total - this._raycaster._stats.mappedObjects.dynamic;
175 | }
176 | }
177 |
178 | return this;
179 | }
180 | };
181 |
182 | Map.prototype.constructor = Map;
183 |
--------------------------------------------------------------------------------
/src/map/map-line-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for lines*/
2 | /**
3 | * Get array of mapped line's vertices used as rays targets.
4 | *
5 | * @method Raycaster.Map#line.getPoints
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @private
9 | * @since 0.6.0
10 | *
11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
12 | *
13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
14 | */
15 | export function getPoints(ray = false) {
16 | if(!this.active)
17 | return [];
18 | return this._points;
19 | };
20 |
21 | /**
22 | * Get array of mapped line's segments used to test object's intersection with ray.
23 | *
24 | * @method Raycaster.Map#line.getSegments
25 | * @memberof Raycaster.Map
26 | * @instance
27 | * @private
28 | * @since 0.6.0
29 | *
30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
31 | */
32 | export function getSegments() {
33 | if(!this.active)
34 | return [];
35 | return this._segments;
36 | };
37 |
38 | /**
39 | * Update line's map of points and segments.
40 | *
41 | * @method Raycaster.Map#line.updateMap
42 | * @memberof Raycaster.Map
43 | * @instance
44 | * @private
45 | * @since 0.6.0
46 | *
47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
48 | */
49 | export function updateMap() {
50 | if(!this.active)
51 | return this;
52 |
53 | let points = [];
54 | let segments = [];
55 |
56 | //calculate offset based on object position and origin point
57 | let offset = new Phaser.Geom.Point();
58 | offset.x = this.object.x - this.object.displayWidth * this.object.originX;
59 | offset.y = this.object.y - this.object.displayHeight * this.object.originY;
60 | let pointA = this.object.geom.getPointA();
61 | let pointB = this.object.geom.getPointB();
62 |
63 | //calculate positions after object's rotation
64 | let rotation = this.object.rotation;
65 | if(rotation !== 0) {
66 | let vectorA = new Phaser.Geom.Line(this.object.x, this.object.y, pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y);
67 | Phaser.Geom.Line.SetToAngle(vectorA, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vectorA) + rotation, Phaser.Geom.Line.Length(vectorA));
68 | pointA = vectorA.getPointB();
69 |
70 | let vectorB = new Phaser.Geom.Line(this.object.x, this.object.y, pointB.x * this.object.scaleX + offset.x, pointB.y * this.object.scaleY + offset.y);
71 | Phaser.Geom.Line.SetToAngle(vectorB, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vectorB) + rotation, Phaser.Geom.Line.Length(vectorB));
72 | pointB = vectorB.getPointB();
73 |
74 | //set points
75 | points.push(new Phaser.Geom.Point(pointA.x, pointA.y));
76 | points.push(new Phaser.Geom.Point(pointB.x, pointB.y));
77 | //set segment
78 | segments.push(new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y));
79 | }
80 | //if rotation === 0
81 | else {
82 | //set points
83 | points.push(new Phaser.Geom.Point(pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y));
84 | points.push(new Phaser.Geom.Point(pointB.x * this.object.scaleX + offset.x, pointB.y * this.object.scaleY + offset.y));
85 | //set segment
86 | segments.push(new Phaser.Geom.Line(pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y, pointB.x + offset.x * this.object.scaleX, pointB.y * this.object.scaleY + offset.y));
87 | }
88 |
89 | //assign neighbours
90 | points[0].neighbours = [points[1]];
91 | points[1].neighbours = [points[0]];
92 |
93 | this._points = points;
94 | this._segments = segments;
95 | return this;
96 | };
97 |
--------------------------------------------------------------------------------
/src/map/map-matterBody-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for matter body*/
2 | /**
3 | * Get array of mapped matter body's vertices used as rays targets.
4 | *
5 | * @method Raycaster.Map#matterBody.getPoints
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @private
9 | * @since 0.9.0
10 | *
11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
12 | *
13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
14 | */
15 | export function getPoints(ray = false) {
16 | if(!this.active)
17 | return [];
18 |
19 | let body = this.object.type === 'body' || this.object.type === 'composite' ? this.object : this.object.body;
20 |
21 | //calculate tangent rays
22 | if(ray && !this.forceVerticesMapping && body.circleRadius > 0) {
23 | let points = [];
24 | let rayA = new Phaser.Geom.Line();
25 | let rayB = new Phaser.Geom.Line();
26 | let c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, body.position.x, body.position.y);
27 |
28 | let rayLength = Math.sqrt(Math.pow(Phaser.Geom.Line.Length(c), 2) - Math.pow(body.circleRadius * body.scale.x, 2));
29 |
30 | //ray angle
31 | let angle = Phaser.Geom.Line.Angle(c);
32 | let dAngle = Math.asin((body.circleRadius * body.scale.x) / Phaser.Geom.Line.Length(c));
33 | Phaser.Geom.Line.SetToAngle(rayA, ray.origin.x, ray.origin.y, angle - dAngle, rayLength);
34 | Phaser.Geom.Line.SetToAngle(rayB, ray.origin.x, ray.origin.y, angle + dAngle, rayLength);
35 |
36 | //adding tangent points
37 | points.push(rayA.getPointB(), rayB.getPointB());
38 |
39 | return points;
40 | }
41 |
42 | return this._points;
43 | };
44 |
45 | /**
46 | * Get array of mapped matter body's segments used to test object's intersection with ray.
47 | *
48 | * @method Raycaster.Map#matterBody.getSegments
49 | * @memberof Raycaster.Map
50 | * @instance
51 | * @private
52 | * @since 0.9.0
53 | *
54 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
55 | */
56 | export function getSegments() {
57 | if(!this.active)
58 | return [];
59 | return this._segments;
60 | };
61 |
62 | /**
63 | * Update matter body's map of points and segments.
64 | *
65 | * @method Raycaster.Map#matterBody.updateMap
66 | * @memberof Raycaster.Map
67 | * @instance
68 | * @private
69 | * @since 0.9.0
70 | *
71 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
72 | */
73 | export function updateMap() {
74 | if(!this.active)
75 | return this;
76 |
77 | let points = [];
78 | let segments = [];
79 | let body = this.object.type === 'body' || this.object.type === 'composite' ? this.object : this.object.body;
80 | let bodies = [body];
81 | let generateBounds = false;
82 |
83 | if(body.circleRadius > 0 && !this.forceVerticesMapping) {
84 | this.circle = true;
85 | this._points = points;
86 | this._segments = segments;
87 |
88 | return this;
89 | }
90 |
91 | this.circle = false;
92 |
93 | if(body.type == 'composite')
94 | bodies = body.bodies;
95 |
96 | if( ( body.bounds === undefined && body.type == 'composite' ) || ( body.type == 'composite' && this.dynamic ) ) {
97 | generateBounds = true;
98 | }
99 |
100 | for(let bodyItem of bodies) {
101 | //if convex body
102 | if(bodyItem.parts.length === 1 || this.forceConvex) {
103 | let vertices = bodyItem.parts[0].vertices;
104 |
105 | points.push(new Phaser.Geom.Point(vertices[0].x, vertices[0].y));
106 | points[0].neighbours = [];
107 |
108 | for(let i = 1, length = vertices.length; i < length; i++) {
109 | let pointA = points.slice(-1)[0],
110 | pointB = new Phaser.Geom.Point(vertices[i].x, vertices[i].y);
111 |
112 | if(!pointA.neighbours)
113 | pointA.neighbours = [];
114 | pointA.neighbours.push(pointB);
115 | pointB.neighbours = [pointA];
116 |
117 | points.push(pointB);
118 |
119 | //add segment
120 | let segment = new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y);
121 | segments.push(segment);
122 | }
123 |
124 | //closing segment
125 | let segment = new Phaser.Geom.Line(vertices[vertices.length - 1].x, vertices[vertices.length - 1].y, vertices[0].x, vertices[0].y);
126 | segments.push(segment);
127 |
128 | points[0].neighbours.push(points.slice(-1)[0]);
129 | }
130 |
131 | //if concave body
132 | else {
133 | let parts = [],
134 | indexedPoints = [];
135 |
136 | for(let i = 1, iLength = bodyItem.parts.length; i < iLength; i++) {
137 | let vertices = bodyItem.parts[i].vertices,
138 | part = [];
139 |
140 | for(let j = 0, jLength = vertices.length; j < jLength; j++) {
141 | let point = new Phaser.Geom.Point(vertices[j].x, vertices[j].y);
142 |
143 | if(part.length) {
144 | let prevPoint = part.slice(-1)[0];
145 | point.neighbours = [prevPoint];
146 | prevPoint.neighbours.push(point);
147 | }
148 | else {
149 | point.neighbours = [];
150 | }
151 |
152 | let index = vertices[j].x + '/' + vertices[j].y;
153 | if(indexedPoints[index] === undefined) {
154 | points.push(point);
155 | indexedPoints[index] = point;
156 | }
157 | else {
158 | indexedPoints[index].neighbours.push(point);
159 | point.neighbours.push(indexedPoints[index]);
160 | }
161 |
162 | part.push(point);
163 |
164 | if(vertices[j].isInternal) {
165 | parts.push(part);
166 | part = [];
167 | }
168 | }
169 | parts.push(part);
170 | }
171 |
172 | for(let part of parts) {
173 | let i = 0,
174 | iLength;
175 | for(i = 0, iLength = part.length - 1; i < iLength; i++) {
176 | segments.push(new Phaser.Geom.Line(part[i].x, part[i].y, part[i+1].x, part[i+1].y));
177 | }
178 | }
179 | }
180 | }
181 |
182 | this._points = points;
183 | this._segments = segments;
184 |
185 | if(generateBounds) {
186 | let bounds = this._raycaster.scene.matter.composite.bounds(body);
187 | body.bounds = bounds;
188 | }
189 |
190 | return this;
191 | };
192 |
193 | /**
194 | * Get matter body's bounding box.
195 | *
196 | * @method Raycaster.Map#matterBody.getBoundingBox
197 | * @memberof Raycaster.Map
198 | * @instance
199 | * @private
200 | * @since 0.9.0
201 | *
202 | * @return {Phaser.Geom.Rectangle} - Matter body's bounding box.
203 | */
204 | export function getBoundingBox() {
205 | let bounds = this.object.type === 'body' || this.object.type === 'composite' ? this.object.bounds : this.object.body.bounds;
206 |
207 | return new Phaser.Geom.Rectangle(bounds.min.x, bounds.min.y, bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y);
208 | }
209 |
210 |
--------------------------------------------------------------------------------
/src/map/map-polygon-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for polygons*/
2 | /**
3 | * Get array of mapped polygon's vertices used as rays targets.
4 | *
5 | * @method Raycaster.Map#polygon.getPoints
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @private
9 | * @since 0.6.0
10 | *
11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
12 | *
13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
14 | */
15 | export function getPoints(ray = false) {
16 | if(!this.active)
17 | return [];
18 | return this._points;
19 | };
20 |
21 | /**
22 | * Get array of mapped polygon's segments used to test object's intersection with ray.
23 | *
24 | * @method Raycaster.Map#polygon.getSegments
25 | * @memberof Raycaster.Map
26 | * @instance
27 | * @private
28 | * @since 0.6.0
29 | *
30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
31 | */
32 | export function getSegments() {
33 | if(!this.active)
34 | return [];
35 | return this._segments;
36 | };
37 |
38 | /**
39 | * Update polygon's map of points and segments.
40 | *
41 | * @method Raycaster.Map#polygon.updateMap
42 | * @memberof Raycaster.Map
43 | * @instance
44 | * @private
45 | * @since 0.6.0
46 | *
47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
48 | */
49 | export function updateMap() {
50 | if(!this.active)
51 | return this;
52 |
53 | let points = [];
54 | let segments = [];
55 |
56 | //calculate offset based on object position and origin point
57 | let offset = new Phaser.Geom.Point();
58 | offset.x = this.object.x - this.object.displayWidth * this.object.originX;
59 | offset.y = this.object.y - this.object.displayHeight * this.object.originY;
60 | //set points
61 | //calculate positions after object's rotation
62 | let rotation = this.object.rotation;
63 | if(rotation !== 0) {
64 | for(let point of this.object.geom.points) {
65 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, point.x * this.object.scaleX + offset.x, point.y * this.object.scaleY + offset.y);
66 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector));
67 | points.push(vector.getPointB());
68 | }
69 | }
70 | //if rotation === 0
71 | else {
72 | for(let point of this.object.geom.points) {
73 | points.push(new Phaser.Geom.Point(point.x * this.object.scaleX + offset.x, point.y * this.object.scaleY + offset.y));
74 | }
75 | }
76 |
77 | for(let i = 0, length = points.length; i < length; i++) {
78 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0],
79 | nextPoint = i < length - 1 ? points[i + 1] : points[0];
80 |
81 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y));
82 |
83 | points[i].neighbours = [
84 | prevPoint,
85 | nextPoint
86 | ];
87 | }
88 |
89 | //set segments
90 | for(let i = 0, length = points.length; i < length; i++) {
91 | if(i+1 < length)
92 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, points[i+1].x, points[i+1].y));
93 | }
94 | //if polygon is not closed
95 | if(!this.object.closePath) {
96 | segments.pop();
97 | points[0].neighbours.shift();
98 | points[points.length - 1].neighbours.pop();
99 | }
100 |
101 | this._points = points;
102 | this._segments = segments;
103 |
104 | return this;
105 | };
106 |
--------------------------------------------------------------------------------
/src/map/map-rectangle-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for rectangles*/
2 | /**
3 | * Get array of mapped rectangle's vertices used as rays targets.
4 | *
5 | * @method Raycaster.Map#rectangle.getPoints
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @private
9 | * @since 0.6.0
10 | *
11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
12 | *
13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
14 | */
15 | export function getPoints(ray = false) {
16 | if(!this.active)
17 | return [];
18 | return this._points;
19 | };
20 |
21 | /**
22 | * Get array of mapped rectangle's segments used to test object's intersection with ray.
23 | *
24 | * @method Raycaster.Map#rectangle.getSegments
25 | * @memberof Raycaster.Map
26 | * @instance
27 | * @private
28 | * @since 0.6.0
29 | *
30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
31 | */
32 | export function getSegments() {
33 | if(!this.active)
34 | return [];
35 | return this._segments;
36 | };
37 |
38 | /**
39 | * Update rectangle's map of points and segments.
40 | *
41 | * @method Raycaster.Map#rectangle.updateMap
42 | * @memberof Raycaster.Map
43 | * @instance
44 | * @private
45 | * @since 0.6.0
46 | *
47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
48 | */
49 | export function updateMap() {
50 | if(!this.active)
51 | return this;
52 |
53 | let points = [];
54 | let segments = [];
55 |
56 | //set points
57 | points = [
58 | this.object.getTopLeft(),
59 | this.object.getTopRight(),
60 | this.object.getBottomRight(),
61 | this.object.getBottomLeft()
62 | ];
63 |
64 | //set segments
65 | for(let i = 0, length = points.length; i < length; i++) {
66 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0],
67 | nextPoint = i < length - 1 ? points[i + 1] : points[0];
68 |
69 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y));
70 |
71 | points[i].neighbours = [
72 | prevPoint,
73 | nextPoint
74 | ];
75 | }
76 |
77 | this._points = points;
78 | this._segments = segments;
79 |
80 | return this;
81 | };
82 |
--------------------------------------------------------------------------------
/src/map/map-tilemap-methods.js:
--------------------------------------------------------------------------------
1 | /*Map methods for tilemaps*/
2 | /**
3 | * Get array of mapped tilemap's vertices used as rays targets.
4 | *
5 | * @method Raycaster.Map#tilemap.getPoints
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @private
9 | * @since 0.7.3
10 | *
11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
12 | *
13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices.
14 | */
15 | export function getPoints(ray = false) {
16 | if(!this.active)
17 | return [];
18 | if(!ray || ray && (ray.detectionRange == 0 || ray.detectionRange >= Phaser.Math.MAX_SAFE_INTEGER))
19 | return this._points;
20 |
21 | let points = [];
22 | for(let point of this._points) {
23 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, point.x, point.y) <= ray.detectionRange)
24 | points.push(point);
25 | }
26 |
27 | //get intersections between tilemap's segments and ray's detection range edge
28 | let segments = this.getSegments(ray);
29 |
30 | for(let segment of segments) {
31 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, segment.x1, segment.y1) > ray.detectionRange)
32 | points.push(new Phaser.Geom.Point(segment.x1, segment.y1));
33 |
34 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, segment.x2, segment.y2) > ray.detectionRange)
35 | points.push(new Phaser.Geom.Point(segment.x2, segment.y2));
36 | }
37 |
38 | return points;
39 | };
40 |
41 | /**
42 | * Get array of mapped tilemap's segments used to test object's intersection with ray.
43 | *
44 | * @method Raycaster.Map#tilemap.getSegments
45 | * @memberof Raycaster.Map
46 | * @instance
47 | * @private
48 | * @since 0.7.3
49 | *
50 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps.
51 | *
52 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments.
53 | */
54 | export function getSegments(ray = false) {
55 | if(!this.active)
56 | return [];
57 | if(!ray || (ray && (ray.detectionRange == 0 || ray.detectionRange >= Phaser.Math.MAX_SAFE_INTEGER)))
58 | return this._segments;
59 |
60 | let segments = [];
61 | for(let segment of this._segments) {
62 | if(Phaser.Geom.Intersects.LineToCircle(segment, ray.detectionRangeCircle)) {
63 | segments.push(segment);
64 | }
65 | }
66 |
67 | return segments;
68 | };
69 |
70 | /**
71 | * Update tilemap's map of points and segments.
72 | *
73 | * @method Raycaster.Map#tilemap.updateMap
74 | * @memberof Raycaster.Map
75 | * @instance
76 | * @private
77 | * @since 0.7.3
78 | *
79 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
80 | */
81 | export function updateMap() {
82 | if(!this.active)
83 | return this;
84 |
85 | let points = [],
86 | segments = [],
87 | columns = Array(this.object.layer.data[0].length + 1);
88 |
89 | for(let i = 0, iLength = columns.length; i < iLength; i++) {
90 | columns[i] = [];
91 | }
92 |
93 |
94 | //calculate offset based on object position and origin point
95 | let offset = new Phaser.Geom.Point(this.object.x, this.object.y);
96 |
97 | let row = this.object.layer.data[0],
98 | tileWidth = this.object.layer.tileWidth * this.object.scaleX,
99 | tileHeight = this.object.layer.tileHeight * this.object.scaleY,
100 | startPoint,
101 | endPoint;
102 |
103 | //set top horizontal lines
104 | if(this.collisionTiles.includes(row[0].index)) {
105 | startPoint = new Phaser.Geom.Point(offset.x, offset.y);
106 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, offset.y);
107 |
108 | columns[0].push(startPoint);
109 | }
110 |
111 | for(let i = 1, iLength = row.length; i < iLength; i++) {
112 | let tile = row[i];
113 |
114 | if(!this.collisionTiles.includes(tile.index)) {
115 | if(startPoint) {
116 | startPoint.neighbours = [endPoint];
117 | endPoint.neighbours = [startPoint];
118 |
119 | points.push(startPoint, endPoint);
120 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
121 |
122 | columns[i].push(endPoint);
123 |
124 | startPoint = false;
125 | endPoint = false;
126 | }
127 | continue;
128 | }
129 |
130 | let x = i * tileWidth + offset.x,
131 | y = offset.y;
132 |
133 | if(!startPoint) {
134 | startPoint = new Phaser.Geom.Point(x, y);
135 | columns[i].push(startPoint);
136 | }
137 |
138 | if(!endPoint) {
139 | endPoint = new Phaser.Geom.Point(x + tileWidth, y);
140 | }
141 | else {
142 | endPoint.x = x + tileWidth;
143 | }
144 | }
145 |
146 | if(startPoint) {
147 | startPoint.neighbours = [endPoint];
148 | endPoint.neighbours = [startPoint];
149 |
150 | points.push(startPoint, endPoint);
151 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
152 |
153 | columns[row.length].push(endPoint);
154 | }
155 |
156 | startPoint = false;
157 | endPoint = false;
158 |
159 | for(let i = 1, iLength = this.object.layer.data.length; i < iLength; i++) {
160 | row = this.object.layer.data[i];
161 | let higherRow = this.object.layer.data[i - 1];
162 |
163 | if(this.collisionTiles.includes(row[0].index) != this.collisionTiles.includes(higherRow[0].index)) {
164 | startPoint = new Phaser.Geom.Point(offset.x, i * tileHeight + offset.y);
165 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, i * tileHeight + offset.y);
166 |
167 | columns[0].push(startPoint);
168 | }
169 |
170 | for(let j = 1, jLength = row.length; j < jLength; j++) {
171 | let tile = row[j],
172 | isCollisionTile = this.collisionTiles.includes(tile.index),
173 | isCollisionHigherTile = this.collisionTiles.includes(higherRow[j].index);
174 |
175 | if(isCollisionTile == isCollisionHigherTile) {
176 | if(startPoint) {
177 | startPoint.neighbours = [endPoint];
178 | endPoint.neighbours = [startPoint];
179 |
180 | points.push(startPoint, endPoint);
181 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
182 |
183 | columns[j].push(endPoint);
184 |
185 | startPoint = false;
186 | endPoint = false;
187 | }
188 | continue;
189 | }
190 |
191 | let x = j * tileWidth + offset.x,
192 | y = i * tileHeight + offset.y;
193 |
194 | if(!startPoint) {
195 | startPoint = new Phaser.Geom.Point(x, y);
196 |
197 | columns[j].push(startPoint);
198 | }
199 |
200 | if(!endPoint) {
201 | endPoint = new Phaser.Geom.Point(x + tileWidth, y);
202 | }
203 | else {
204 | endPoint.x = x + tileWidth;
205 | }
206 | }
207 |
208 | if(startPoint) {
209 | startPoint.neighbours = [endPoint];
210 | endPoint.neighbours = [startPoint];
211 |
212 | points.push(startPoint, endPoint);
213 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
214 |
215 | columns[row.length].push(endPoint);
216 | }
217 |
218 | startPoint = false;
219 | endPoint = false;
220 | }
221 |
222 | //set bottom horizontal lines
223 | row = this.object.layer.data.slice(-1)[0];
224 | let y = this.object.layer.data.length * tileHeight + offset.y;
225 |
226 | if(this.collisionTiles.includes(row[0].index)) {
227 | startPoint = new Phaser.Geom.Point(offset.x, y);
228 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, y);
229 |
230 | columns[0].push(startPoint);
231 | }
232 |
233 | for(let i = 1, iLength = row.length; i < iLength; i++) {
234 | let tile = row[i];
235 |
236 | if(!this.collisionTiles.includes(tile.index)) {
237 | if(startPoint) {
238 | startPoint.neighbours = [endPoint];
239 | endPoint.neighbours = [startPoint];
240 |
241 | points.push(startPoint, endPoint);
242 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
243 |
244 | columns[i].push(endPoint);
245 |
246 | startPoint = false;
247 | endPoint = false;
248 | }
249 | continue;
250 | }
251 |
252 | let x = i * tileWidth + offset.x;
253 |
254 | if(!startPoint) {
255 | startPoint = new Phaser.Geom.Point(x, y);
256 |
257 | columns[i].push(startPoint);
258 | }
259 |
260 | if(!endPoint) {
261 | endPoint = new Phaser.Geom.Point(x + tileWidth, y);
262 | }
263 | else {
264 | endPoint.x = x + tileWidth;
265 | }
266 | }
267 |
268 | if(startPoint) {
269 | startPoint.neighbours = [endPoint];
270 | endPoint.neighbours = [startPoint];
271 |
272 | points.push(startPoint, endPoint);
273 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y));
274 |
275 | columns[row.length].push(endPoint);
276 | }
277 |
278 | //set vertical lines
279 | for(let i = 0, iLength = columns.length; i < iLength; i++) {
280 | const column = columns[i];
281 |
282 | for(let j = 0, jLength = column.length - 1; j < jLength; j++) {
283 | segments.push(new Phaser.Geom.Line(column[j].x, column[j].y, column[j+1].x, column[j+1].y));
284 | column[j].neighbours.push(column[j+1]);
285 | column[j+1].neighbours.push(column[j]);
286 | j++;
287 | }
288 | }
289 |
290 | this._points = points;
291 | this._segments = segments;
292 | return this;
293 | };
294 |
295 | /**
296 | * Set tile types which should be mapped (for Phaser.Tilemaps.StaticTilemapLayer and Phaser.Tilemaps.DynamicTilemapLayer maps only).
297 | *
298 | * @method Raycaster.Map#setCollisionTiles
299 | * @memberof Raycaster.Map
300 | * @instance
301 | * @since 0.7.3
302 | *
303 | * @param {array} [tiles = []] - Set of tile's indexes to map.
304 | *
305 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
306 | */
307 | export function setCollisionTiles(tiles = []) {
308 | this.collisionTiles = tiles;
309 | return this;
310 | }
311 |
--------------------------------------------------------------------------------
/src/map/segmentsCount.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set segment count for cirle's map.
3 | * If set to 0, map won't be generating segments and relay only on tangent points calculated for currently testing ray.
4 | *
5 | * @method Raycaster.Map#setSegmentCount
6 | * @memberof Raycaster.Map
7 | * @instance
8 | * @since 0.6.0
9 | *
10 | * @param {number} count - Circle map's segment count.
11 | *
12 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
13 | */
14 | export function setSegmentCount(count) {
15 | this.segmentCount = count;
16 | this.circle = count ? false : true;
17 |
18 | this.updateMap();
19 | return this;
20 | }
21 |
--------------------------------------------------------------------------------
/src/ray/angle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set ray's angle (direction) in radians.
3 | *
4 | * @method Raycaster.Ray#setAngle
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {number} [angle = 0] - Ray's angle in radians.
10 | *
11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
12 | */
13 | export function setAngle(angle = 0) {
14 | this.angle = Phaser.Math.Angle.Normalize(angle);
15 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
16 | return this;
17 | }
18 |
19 | /**
20 | * Set ray's angle (direction) in degrees.
21 | *
22 | * @method Raycaster.Ray#setAngleDeg
23 | * @memberof Raycaster.Ray
24 | * @instance
25 | * @since 0.6.1
26 | *
27 | * @param {number} [angle = 0] - Ray's angle in degrees.
28 | *
29 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
30 | */
31 | export function setAngleDeg(angle = 0) {
32 | this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(angle));
33 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
34 | return this;
35 | }
36 |
--------------------------------------------------------------------------------
/src/ray/castCircle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cast ray in all directions to find closest intersections with tested mapped objects.
3 | *
4 | * @method Raycaster.Ray#castCircle
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {object} [options] - options that may include:
10 | * @param {object[]} [options.objects = Raycaster.mappedObjects] - Array of game objects to test. If not provided test all mapped game objects.
11 | *
12 | * @return {Phaser.Geom.Point[]} Array of points of ray's closest intersections with tested objects. Additionally each point contains reference to hit mapped object and it's segment if available.
13 | */
14 | export function castCircle(options = {}) {
15 | let originalAngle = this.angle;
16 | let intersections = [];
17 | let maps = [];
18 | let rayTargets = [];
19 | let testedObjects = [];
20 | let startTime = performance.now();
21 | //reset stats
22 | this._stats = {
23 | method: 'castCircle',
24 | rays: 0,
25 | testedMappedObjects: 0,
26 | hitMappedObjects: 0,
27 | segments: 0,
28 | time: 0
29 | };
30 |
31 | //if no objects to cast ray were passed, use raycasters mapped objects
32 | if(!options.objects) {
33 | if(this._raycaster)
34 | options.objects = this._raycaster.mappedObjects;
35 | else
36 | return intersections;
37 | }
38 |
39 | //if bounding box is defined add bounding box points to
40 | if(this._raycaster && this._raycaster.boundingBox) {
41 | for(let point of this._raycaster.boundingBox.points) {
42 | rayTargets.push({
43 | point: point,
44 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, point.x, point.y)
45 | });
46 | }
47 | }
48 |
49 | for(let i=0, iLength = options.objects.length; i < iLength; i++) {
50 | let object = options.objects[i];
51 | //if bound in range
52 | if(!this.boundsInRange(object))
53 | continue;
54 |
55 | testedObjects.push(object);
56 |
57 | let map, boundingBox;
58 | if(object.type === 'body' || object.type === 'composite')
59 | map = object.raycasterMap;
60 | else
61 | map = object.data.get('raycasterMap');
62 |
63 | //get slightly enlarged bounding box due to fridge cases, when ray "glanced" border box's corner (v0.10.1)
64 | boundingBox = map.getBoundingBox();
65 | boundingBox.setTo(boundingBox.x - 0.1, boundingBox.y - 0.1, boundingBox.width + 0.2, boundingBox.height + 0.2);
66 |
67 | map._boundingBox = boundingBox;
68 |
69 | maps.push(map);
70 | //get points and angles
71 | for(let point of map.getPoints(this)) {
72 | rayTargets.push({
73 | point: point,
74 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, point.x, point.y)
75 | });
76 | }
77 |
78 | //get objects intersections
79 | for(let j = i+1, jLength = options.objects.length; j < jLength; j++){
80 | let objectB = options.objects[j];
81 | let mapB;
82 | if(objectB.type === 'body' || objectB.type === 'composite')
83 | mapB = objectB.raycasterMap;
84 | else {
85 | mapB = objectB.data.get('raycasterMap');
86 | }
87 | //check if bounding boxes overlap
88 | if(!Phaser.Geom.Intersects.RectangleToRectangle(map.getBoundingBox(), mapB.getBoundingBox()))
89 | continue;
90 |
91 | //find objects intersections
92 | for(let segmentA of map.getSegments(this)) {
93 | for(let segmentB of mapB.getSegments(this)) {
94 | let intersection = [];
95 | if(!Phaser.Geom.Intersects.LineToLine(segmentA, segmentB, intersection))
96 | continue;
97 |
98 | let target = {
99 | point: new Phaser.Geom.Point(intersection.x, intersection.y),
100 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, intersection.x, intersection.y)
101 | };
102 | target.point.intersection = false;
103 | rayTargets.push(target);
104 | }
105 | }
106 | }
107 | }
108 |
109 | //sort target points by angle
110 | rayTargets.sort(function(a, b){
111 | //if rays towards points have the same angles promote closer one
112 | if(a.angle == b.angle) {
113 | if(Phaser.Math.Distance.Between(this.origin.x, this.origin.y, a.point.x, a.point.y) > Phaser.Math.Distance.Between(this.origin.x, this.origin.y, b.point.x, b.point.y))
114 | return 1;
115 | else
116 | return -1;
117 | }
118 |
119 | return a.angle - b.angle;
120 | }.bind(this));
121 |
122 | let previousTarget = {
123 | angle: false
124 | };
125 |
126 | //cast rays
127 | for(let target of rayTargets){
128 | //if current target is the same as previous one skip loop
129 | if(target.angle === previousTarget.angle) {
130 | continue;
131 | }
132 |
133 | previousTarget = target;
134 |
135 | this.setAngle(target.angle);
136 | let intersection = this.cast({
137 | objects: testedObjects,
138 | target: target.point,
139 | internal: true
140 | });
141 |
142 | if(intersection) {
143 | //if intersection hits target point check if ray "glanced" mapped object.
144 | let castSides = false;
145 | if(this.round) {
146 | let roundedTarget = new Phaser.Geom.Point(Math.round(target.point.x), Math.round(target.point.y));
147 | castSides = Phaser.Geom.Point.Equals(roundedTarget, intersection)
148 | }
149 | else {
150 | castSides = Phaser.Geom.Point.Equals(target.point, intersection);
151 | }
152 |
153 | if(!castSides) {
154 | //castSides = false;
155 | }
156 | else if(!target.point.neighbours || target.point.neighbours.length < 2) {
157 | //castSides = true;
158 | }
159 | //check if ray and at least one line between target point and it's neighbours are parallel
160 | else if(Phaser.Math.Angle.Normalize(this.angle - Phaser.Math.Angle.BetweenPoints(this.origin, target.point.neighbours[0])) < 0.0001
161 | || Phaser.Math.Angle.Normalize(this.angle - Phaser.Math.Angle.BetweenPoints(this.origin, target.point.neighbours[1])) < 0.0001) {
162 | //castSides = true;
163 | }
164 | //check if ray crossed more than 1 points of triangle created by tatget point and it's neighbours
165 | else {
166 | let triangleIntersections = [];
167 | if(!target.point.neighboursTriangle) {
168 | target.point.neighboursTriangle = new Phaser.Geom.Triangle(target.point.x, target.point.y, target.point.neighbours[0].x, target.point.neighbours[0].y, target.point.neighbours[1].x, target.point.neighbours[1].y);
169 | }
170 |
171 | Phaser.Geom.Intersects.GetTriangleToLine(target.point.neighboursTriangle, this._ray, triangleIntersections);
172 |
173 | //if point of intersection of ray and tirangle are close to target point, assume ray "glanced" triangle.
174 | for(let triangleIntersection of triangleIntersections) {
175 | if(Math.abs(target.point.x - triangleIntersection.x) > 0.0001 && Math.abs(target.point.y - triangleIntersection.y) > 0.0001) {
176 | castSides = false;
177 | break;
178 | }
179 | }
180 | }
181 |
182 | //if ray "glanced" mapped object cast two additional rays
183 | if(castSides) {
184 | this.setAngle(target.angle - 0.0001);
185 | let intersectionA = this.cast({
186 | objects: testedObjects,
187 | internal: true
188 | });
189 |
190 | if(intersectionA) {
191 | intersections.push(intersectionA);
192 | }
193 |
194 | intersections.push(intersection);
195 |
196 | this.setAngle(target.angle + 0.0001);
197 | let intersectionB = this.cast({
198 | objects: testedObjects,
199 | internal: true
200 | });
201 |
202 | if(intersectionB) {
203 | intersections.push(intersectionB);
204 | }
205 |
206 | continue;
207 | }
208 |
209 | intersections.push(intersection);
210 | }
211 | }
212 |
213 | this.setAngle(originalAngle);
214 | this.intersections = intersections;
215 |
216 | if(this.autoSlice)
217 | this.slicedIntersections = this.slice();
218 |
219 | this._stats.time = performance.now() - startTime;
220 |
221 | this.drawDebug(intersections);
222 |
223 | return intersections;
224 | }
225 |
--------------------------------------------------------------------------------
/src/ray/cone.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set ray's cone angle (width) in radians.
3 | *
4 | * @method Raycaster.Ray#setCone
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.7.0
8 | *
9 | * @param {number} [cone = 0] - Ray's cone angle in radians.
10 | *
11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
12 | */
13 | export function setCone(cone = 0) {
14 | this.cone = cone;
15 | return this;
16 | }
17 |
18 | /**
19 | * Set ray's cone angle (width) in degrees.
20 | *
21 | * @method Raycaster.Ray#setConeDeg
22 | * @memberof Raycaster.Ray
23 | * @instance
24 | * @since 0.7.0
25 | *
26 | * @param {number} [cone = 0] - Ray's cone angle in degrees.
27 | *
28 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
29 | */
30 | export function setConeDeg(cone = 0) {
31 | this.cone = Phaser.Math.DegToRad(cone);
32 | return this;
33 | }
34 |
--------------------------------------------------------------------------------
/src/ray/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Configure ray.
3 | *
4 | * @method Raycaster.Ray#config
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {object} [options] - Ray's congfiguration options. May include:
10 | * @param {Phaser.Geom.Point|Point} [options.origin = {x:0, y:0}] - Ray's position.
11 | * @param {number} [options.angle = 0] - Ray's angle in radians.
12 | * @param {number} [options.angleDeg = 0] - Ray's angle in degrees.
13 | * @param {number} [options.cone = 0] - Ray's cone angle in radians.
14 | * @param {number} [options.coneDeg = 0] - Ray's cone angle in degrees.
15 | * @param {number} [options.range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
16 | * @param {number} [options.collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's maximum collision range of ray's field of view.
17 | * @param {number} [options.detectionRange = Phaser.Math.MAX_SAFE_INTEGER] - Maximum distance between ray's position and tested objects bounding boxes.
18 | * @param {boolean} [options.ignoreNotIntersectedRays = true] - If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position.
19 | * @param {boolean} [options.autoSlice = false] - If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}.
20 | * @param {boolean} [options.round = false] - If set true, point where ray hit will be rounded.
21 | * @param {(boolean|'arcade'|'matter')} [options.enablePhysics = false] - Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. If set true, arcade physics body will be added.
22 | *
23 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
24 | */
25 | export function config(options) {
26 | this.object = options.object;
27 | //origin
28 | if(options.origin !== undefined)
29 | this.origin.setTo(options.origin.x, options.origin.y);
30 |
31 | //angle
32 | if(options.angle !== undefined)
33 | this.angle = Phaser.Math.Angle.Normalize(options.angle);
34 |
35 | //angle deg
36 | if(options.angleDeg !== undefined)
37 | this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(options.angleDeg));
38 |
39 | //cone angle
40 | if(options.cone !== undefined)
41 | this.cone = options.cone;
42 |
43 | //cone angle deg
44 | if(options.coneDeg !== undefined)
45 | this.cone = Phaser.Math.DegToRad(options.coneDeg);
46 |
47 | //ray range (0 = max)
48 | if(options.rayRange !== undefined)
49 | this.rayRange = options.rayRange;
50 |
51 | //collision range (0 = max)
52 | if(options.collisionRange !== undefined)
53 | this.collisionRange = options.collisionRange;
54 |
55 | //detection range (0 = max)
56 | if(options.detectionRange !== undefined)
57 | this.detectionRange = options.detectionRange;
58 |
59 | //ignore not intersected rays
60 | if(options.ignoreNotIntersectedRays !== undefined)
61 | this.ignoreNotIntersectedRays = (options.ignoreNotIntersectedRays == true)
62 |
63 | //round
64 | if(options.round !== undefined)
65 | this.round = (options.round == true)
66 |
67 | //auto slice
68 | if(options.autoSlice !== undefined)
69 | this.autoSlice = (options.autoSlice == true)
70 |
71 | //enable physics
72 | if(options.enablePhysics !== undefined && options.enablePhysics)
73 | this.enablePhysics(options.enablePhysics);
74 |
75 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
76 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
77 |
78 | if(this._raycaster.debugOptions.enabled && this._raycaster.scene !== undefined) {
79 | this.graphics = this._raycaster.scene.add.graphics({ lineStyle: { width: 1, color: 0x00ff00}, fillStyle: { color: 0xff00ff } });
80 | this.graphics.setDepth(1000);
81 | }
82 |
83 | return this;
84 | }
85 |
--------------------------------------------------------------------------------
/src/ray/debug.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Draw rays in debug mode
3 | *
4 | * @method Raycaster.Ray#drawDebug
5 | * @memberof Raycaster
6 | * @private
7 | * @since 0.10.0
8 | *
9 | * @param {Phaser.Geom.Point[]} Array of points of ray's closest intersections with tested objects.
10 | *
11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
12 | */
13 | export function drawDebug(intersections) {
14 | if(this.graphics === undefined || !this._raycaster.debugOptions.enabled)
15 | return this;
16 |
17 | //clear
18 | this.graphics.clear();
19 |
20 | if(!this._raycaster.debugOptions.rays)
21 | return this;
22 |
23 | if(this._raycaster.debugOptions.graphics.ray) {
24 | this.graphics.lineStyle(1, this._raycaster.debugOptions.graphics.ray);
25 |
26 | for(let intersection of intersections) {
27 | this.graphics.strokeLineShape({
28 | x1: this.origin.x,
29 | y1: this.origin.y,
30 | x2: intersection.x,
31 | y2: intersection.y
32 | });
33 | }
34 | }
35 |
36 | if(this._raycaster.debugOptions.graphics.rayPoint) {
37 | this.graphics.fillStyle(this._raycaster.debugOptions.graphics.rayPoint);
38 |
39 | this.graphics.fillPoint(this.origin.x, this.origin.y, 3);
40 |
41 | for(let intersection of intersections) {
42 | this.graphics.fillPoint(intersection.x, intersection.y, 3);
43 | }
44 | }
45 |
46 | return this;
47 | }
--------------------------------------------------------------------------------
/src/ray/destroy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Destroy object
3 | *
4 | * @method Raycaster.Ray#destroy
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.10.3
8 | */
9 | export function destroy() {
10 | if(this.graphics)
11 | this.graphics.destroy();
12 |
13 | for(let key in this) {
14 | delete this[key];
15 | }
16 | }
--------------------------------------------------------------------------------
/src/ray/enablePhysics.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. Physics body can be added only once.
3 | *
4 | * @method Raycaster.Ray#enablePhysics
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.8.0
8 | *
9 | * @param {'arcade'|'matter'} [type = 'arcade'] - Physics type
10 | *
11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
12 | */
13 | export function enablePhysics(type = 'arcade') {
14 |
15 | if(this.body !== undefined)
16 | return this;
17 |
18 | this.collisionCircle = this._raycaster.scene.add.circle(this.origin.x, this.origin.y, this.collisionRange);
19 | this.collisionCircle._ray = this;
20 |
21 | if(type === 'matter') {
22 | this.bodyType = 'matter';
23 |
24 | if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) {
25 | let bounds = this._raycaster.boundingBox;
26 | this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'rectangle', x:bounds.rectangle.centerX, y:bounds.rectangle.centerY, width:bounds.rectangle.width, height:bounds.rectangle.height }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true });
27 | }
28 | else {
29 | this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'circle' }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true });
30 | }
31 |
32 | this.body = this.collisionCircle.body;
33 | this.body._ray = this;
34 | this.setOnCollideActive();
35 | }
36 | else {
37 | this.bodyType = 'arcade';
38 | this._raycaster.scene.physics.add.existing(this.collisionCircle);
39 |
40 | this.body = this.collisionCircle.body;
41 | this.body
42 | .setCircle(this.collisionRange)
43 | .setAllowGravity(false)
44 | .setImmovable(true);
45 | this.body._ray = this;
46 | }
47 |
48 | return this;
49 | }
50 |
--------------------------------------------------------------------------------
/src/ray/matter-physics-methods.js:
--------------------------------------------------------------------------------
1 | /*Matter physics methods for ray body*/
2 | /**
3 | * Sets the collision category of this ray's Matter Body. This number must be a power of two between 2^0 (= 1) and 2^31.
4 | * Two bodies with different collision groups (see {@link #setCollisionGroup}) will only collide if their collision
5 | * categories are included in their collision masks (see {@link #setCollidesWith}).
6 | *
7 | * @method Raycaster.Ray#setCollisionCategory
8 | * @memberof Raycaster.Ray
9 | * @instance
10 | * @since 0.9.1
11 | *
12 | * @param {number} value - Unique category bitfield.
13 | *
14 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
15 | */
16 | export function setCollisionCategory(value) {
17 | this.body.collisionFilter.category = value;
18 |
19 | return this;
20 | };
21 |
22 | /**
23 | * Sets the collision group of this ray's Matter Body. If this is zero or two Matter Bodies have different values,
24 | * they will collide according to the usual rules (see {@link #setCollisionCategory} and {@link #setCollisionGroup}).
25 | * If two Matter Bodies have the same positive value, they will always collide; if they have the same negative value,
26 | * they will never collide.
27 | *
28 | * @method Raycaster.Ray#setCollisionCategory
29 | * @memberof Raycaster.Ray
30 | * @instance
31 | * @since 0.9.1
32 | *
33 | * @param {number} value - Unique group index.
34 | *
35 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
36 | */
37 | export function setCollisionGroup(value) {
38 | this.body.collisionFilter.group = value;
39 |
40 | return this;
41 | };
42 |
43 | /**
44 | * Sets the collision mask for this ray's Matter Body. Two Matter Bodies with different collision groups will only
45 | * collide if each one includes the other's category in its mask based on a bitwise AND, i.e. `(categoryA & maskB) !== 0`
46 | * and `(categoryB & maskA) !== 0` are both true.*
47 | *
48 | * @method Raycaster.Ray#setCollidesWith
49 | * @memberof Raycaster.Ray
50 | * @instance
51 | * @since 0.9.1
52 | *
53 | * @param {(number|number[])} categories - A unique category bitfield, or an array of them.
54 | *
55 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
56 | */
57 |
58 | export function setCollidesWith(categories) {
59 | var flags = 0;
60 |
61 | if (!Array.isArray(categories))
62 | {
63 | flags = categories;
64 | }
65 | else
66 | {
67 | for (var i = 0; i < categories.length; i++)
68 | {
69 | flags |= categories[i];
70 | }
71 | }
72 |
73 | this.body.collisionFilter.mask = flags;
74 |
75 | return this;
76 | };
77 |
78 | /**
79 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object.
80 | *
81 | * This does not change the bodies collision category, group or filter. Those must be set in addition
82 | * to the callback.
83 | *
84 | * @method Raycaster.Ray#setOnCollide
85 | * @memberof Raycaster.Ray
86 | * @instance
87 | * @since 0.9.1
88 | *
89 | * @param {function} callback - The callback to invoke when this body starts colliding with another.
90 | *
91 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
92 | */
93 | export function setOnCollide(callback) {
94 | let self = this;
95 | this.body.onCollideCallback = function(collisionInfo) {
96 | if(collisionInfo.rayCollided) {
97 | callback(collisionInfo);
98 | }
99 | else if(self.processOverlap(collisionInfo)) {
100 | collisionInfo.rayCollided = true;
101 | callback(collisionInfo);
102 | }
103 | };
104 |
105 | return this;
106 | };
107 |
108 | /**
109 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object.
110 | *
111 | * This does not change the bodies collision category, group or filter. Those must be set in addition
112 | * to the callback.
113 | *
114 | * @method Raycaster.Ray#setOnCollideEnd
115 | * @memberof Raycaster.Ray
116 | * @instance
117 | * @since 0.9.1
118 | *
119 | * @param {function} callback - The callback to invoke when this body stops colliding with another.
120 | *
121 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
122 | */
123 | export function setOnCollideEnd(callback) {
124 | this.body.onCollideEndCallback = function(collisionInfo) {
125 | if(collisionInfo.rayCollided) {
126 | collisionInfo.rayCollided = false;
127 | callback(collisionInfo);
128 | }
129 | }
130 |
131 | return this;
132 | };
133 |
134 | /**
135 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object.
136 | *
137 | * This does not change the bodies collision category, group or filter. Those must be set in addition
138 | * to the callback.
139 | *
140 | * @method Raycaster.Ray#setOnCollideActive
141 | * @memberof Raycaster.Ray
142 | * @instance
143 | * @since 0.9.1
144 | *
145 | * @param {function} callback - The callback to invoke for the duration of this body colliding with another.
146 | *
147 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
148 | */
149 | export function setOnCollideActive(callback) {
150 | let self = this;
151 | let func = function(collisionInfo) {
152 | if(self.processOverlap(collisionInfo)) {
153 | let body = collisionInfo.bodyA.label === 'phaser-raycaster-ray-body' ? collisionInfo.bodyB : collisionInfo.bodyA;
154 |
155 | if(collisionInfo.rayCollided !== true) {
156 | collisionInfo.rayCollided = true;
157 | if(self.body.onCollideCallback) {
158 | self.body.onCollideCallback(collisionInfo);
159 | }
160 |
161 | if(self.body.onCollideWith !== undefined && self.body.onCollideWith[body.id]) {
162 | self.body.onCollideWith[body.id](body, collisionInfo);
163 | }
164 | }
165 | if(callback)
166 | callback(collisionInfo);
167 | }
168 | else {
169 | if(self.body.onCollideEndCallback && collisionInfo.rayCollided === true) {
170 | self.body.onCollideEndCallback(collisionInfo);
171 | }
172 | }
173 | }
174 |
175 | this.body.onCollideActiveCallback = func;
176 |
177 | return this;
178 | }
179 |
180 | /**
181 | * The callback is sent a reference to the other body, along with a `Phaser.Types.Physics.Matter.MatterCollisionData` object.
182 | *
183 | * This does not change the bodies collision category, group or filter. Those must be set in addition
184 | * to the callback.
185 | *
186 | * @method Raycaster.Ray#setOnCollideWith
187 | * @memberof Raycaster.Ray
188 | * @instance
189 | * @since 0.9.1
190 | *
191 | * @param {(MatterJS.Body|MatterJS.Body[])} body - The body, or an array of bodies, to test for collisions with.
192 | * @param {function} callback - The callback to invoke when this body collides with the given body or bodies.
193 | *
194 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
195 | */
196 | export function setOnCollideWith(body, callback) {
197 | let self = this;
198 | let func = function(body, collisionInfo) {
199 | if(collisionInfo.rayCollided) {
200 | callback(body, collisionInfo);
201 | }
202 | else if(self.processOverlap(collisionInfo)) {
203 | collisionInfo.rayCollided = true;
204 | callback(body, collisionInfo);
205 | }
206 | }
207 |
208 | if (!Array.isArray(body))
209 | {
210 | body = [ body ];
211 | }
212 |
213 | for (var i = 0; i < body.length; i++)
214 | {
215 | var src = (body[i].hasOwnProperty('body')) ? body[i].body : body[i];
216 |
217 | this.body.setOnCollideWith(src, func);
218 | }
219 |
220 | return this;
221 | };
222 |
--------------------------------------------------------------------------------
/src/ray/origin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set ray's source position.
3 | *
4 | * @method Raycaster.Ray#setOrigin
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {number} x - X coordinate.
10 | * @param {number} y - Y coordinate.
11 | *
12 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
13 | */
14 | export function setOrigin(x, y) {
15 | this.origin.setTo(x, y);
16 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
17 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
18 |
19 | if(this.bodyType === 'matter' && this.collisionRange !== Phaser.Math.MAX_SAFE_INTEGER) {
20 | this.collisionCircle.x = x;
21 | this.collisionCircle.y = y;
22 | }
23 | else if(this.bodyType === 'arcade') {
24 | this.collisionCircle.x = x;
25 | this.collisionCircle.y = y;
26 | }
27 |
28 | return this;
29 | }
30 |
--------------------------------------------------------------------------------
/src/ray/overlap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get game objects overlaping field of view.
3 | *
4 | * @method Raycaster.Ray#overlap
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.8.0
8 | *
9 | * @param {object|object[]} [objects] - Game object / array off game objects to test.
10 | *
11 | * @return {object[]} Array of game objects that overlaps with field of view.
12 | */
13 | export function overlap(objects) {
14 | let targets = [];
15 | let overlapCircle = new Phaser.Geom.Circle(this.origin.x, this.origin.y, this.collisionRange);
16 |
17 | //matter physics
18 | if(this.bodyType === 'matter') {
19 | let isCollisionInfo = false;
20 | if(objects === undefined) {
21 | objects = this._raycaster.scene.matter.query.collides(this.body, this._raycaster.scene.matter.getMatterBodies());
22 |
23 | for(let object of objects) {
24 | let body = object.bodyA === this.body ? object.bodyB : object.bodyA;
25 |
26 | if(this.testMatterOverlap(body))
27 | targets.push(body);
28 | }
29 | }
30 | //get object's body
31 | else {
32 | if(!Array.isArray(objects))
33 | objects = [objects];
34 |
35 | for(let object of objects) {
36 | if(object === this.body)
37 | continue;
38 |
39 | if(this.testMatterOverlap(object))
40 | targets.push(object);
41 | }
42 | }
43 | }
44 | //arcade physics
45 | else {
46 | let bodies = false;
47 | //get bodies in range
48 | if(objects === undefined) {
49 | objects = this._raycaster.scene.physics.overlapCirc(this.origin.x, this.origin.y, this.collisionRange, true, true);
50 | bodies = true;
51 | }
52 | //get object's body
53 | else if(!Array.isArray(objects)) {
54 | objects = [objects];
55 | }
56 | //if objects are bodies
57 | if(bodies) {
58 | for(let body of objects) {
59 | if(body === this.body)
60 | continue;
61 |
62 | let hitbox;
63 | //get physics body hitbox
64 | if(body.isCircle) {
65 | hitbox = new Phaser.Geom.Circle(body.position.x + body.halfWidth, body.position.y + body.halfWidth, body.halfWidth);
66 | }
67 | else {
68 | hitbox = new Phaser.Geom.Rectangle(body.x, body.y, body.width, body.height);
69 | }
70 |
71 | if(this.testArcadeOverlap(hitbox))
72 | targets.push(body.gameObject);
73 | }
74 | }
75 | //if objects are game objects
76 | else {
77 | for(let object of objects) {
78 | if(object.body === undefined)
79 | continue;
80 |
81 | let hitbox;
82 | //get physics body hitbox
83 | if(object.body.isCircle) {
84 | hitbox = new Phaser.Geom.Circle(object.body.position.x + object.body.halfWidth, object.body.position.y + object.body.halfWidth, object.body.halfWidth);
85 | if(!Phaser.Geom.Intersects.CircleToCircle(overlapCircle, hitbox))
86 | continue;
87 | }
88 | else {
89 | hitbox = new Phaser.Geom.Rectangle(object.body.x, object.body.y, object.body.width, object.body.height);
90 | if(!Phaser.Geom.Intersects.CircleToRectangle(overlapCircle, hitbox))
91 | continue;
92 | }
93 |
94 | if(this.testArcadeOverlap(hitbox))
95 | targets.push(object);
96 | }
97 | }
98 | }
99 |
100 | return targets;
101 | }
102 |
103 | /**
104 | * Process callback for physics collider / overlap.
105 | *
106 | * @method Raycaster.Ray#processOverlap
107 | * @memberof Raycaster.Ray
108 | * @instance
109 | * @since 0.8.0
110 | *
111 | * @param {object} object1 - Game object or matter body passed by collider / overlap or matter CollisionInfo object.
112 | * @param {object} object2 - Game object or matter body passed by collider / overlap. Ignored if matter CollisionInfo object was passed as first argument.
113 | *
114 | * @return {boolean} Return true if game object is overlapping ray's field of view.
115 | */
116 | export function processOverlap(object1, object2) {
117 | let obj1, obj2, target;
118 | //check if it's matter collisionInfo object
119 | if(object1.bodyA !== undefined && object1.bodyB !== undefined) {
120 | obj1 = object1.bodyA;
121 | obj2 = object1.bodyB;
122 | }
123 | else {
124 | obj1 = object1;
125 | obj2 = object2;
126 | }
127 |
128 | if(obj1._ray !== undefined && obj1._ray === this)
129 | target = obj2;
130 | else if(obj2._ray !== undefined && obj2._ray === this)
131 | target = obj1;
132 | else
133 | return false;
134 |
135 | return (this.overlap(target).length > 0);
136 | }
137 |
138 | /**
139 | * Test if hitbox overlaps with field of view. Method used in {@link Raycaster.Ray#overlap Ray.overlap}.
140 | *
141 | * @method Raycaster.Ray#testArcadeOverlap
142 | * @memberof Raycaster.Ray
143 | * @instance
144 | * @private
145 | * @since 0.8.0
146 | *
147 | * @param {object} hitbox - Game object's hitbox generated inside {@link Raycaster.Ray#overlap Ray.overlap}.
148 | *
149 | * @return {boolean} True if hitbox overlaps with {@link Raycaster.Ray Raycaster.Ray} field of view.
150 | */
151 | export function testArcadeOverlap(hitbox) {
152 | let overlap = false;
153 |
154 | //iterate through field of view slices to check collisions with target
155 | for(let slice of this.slicedIntersections) {
156 | //if hitbox is a circle
157 | if(hitbox.type == 0) {
158 | overlap = Phaser.Geom.Intersects.TriangleToCircle(slice, hitbox);
159 | }
160 | //if hitbox is a rectangle
161 | else {
162 | overlap = Phaser.Geom.Intersects.RectangleToTriangle(hitbox, slice);
163 | }
164 |
165 | if(overlap) {
166 | return true;
167 | }
168 | }
169 |
170 | return false;
171 | }
172 |
173 | /**
174 | * Test if matter body overlaps with field of view. Method used in {@link Raycaster.Ray#overlap Ray.overlap}.
175 | *
176 | * @method Raycaster.Ray#testMatterOverlap
177 | * @memberof Raycaster.Ray
178 | * @instance
179 | * @private
180 | * @since 0.9.0
181 | *
182 | * @param {object} body - Matter body.
183 | *
184 | * @return {boolean} True if body overlaps with {@link Raycaster.Ray Raycaster.Ray} field of view.
185 | */
186 | export function testMatterOverlap(object) {
187 | let body;
188 |
189 | if(object.type === 'body')
190 | body = object;
191 | else if(object.body !== undefined)
192 | body = object.body;
193 | else
194 | return false;
195 |
196 | //if body is concave, ignore convex body
197 | let parts = body.parts.length > 1 ? body.parts.splice(1) : body.parts;
198 | //iterate through bodies
199 | for(let part of parts) {
200 | let pointA = part.vertices[0];
201 |
202 | for(let i = 1, length = part.vertices.length; i < length; i++) {
203 | let pointB = part.vertices[i];
204 | let segment = new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y);
205 |
206 | //iterate through field of view slices to check collisions with target
207 | for(let slice of this.slicedIntersections) {
208 | let overlap = Phaser.Geom.Intersects.TriangleToLine(slice, segment);
209 | //additional checking if slice contain segment's points due to TriangleToLine bug.
210 | if(!overlap)
211 | overlap = Phaser.Geom.Triangle.ContainsPoint(slice, segment.getPointA());
212 | if(!overlap)
213 | overlap = Phaser.Geom.Triangle.ContainsPoint(slice, segment.getPointB());
214 |
215 | if(overlap) {
216 | return true;
217 | }
218 | }
219 | pointA = pointB;
220 | }
221 |
222 | //closing segment
223 | let segment = new Phaser.Geom.Line(part.vertices[part.vertices.length - 1].x, part.vertices[part.vertices.length - 1].y, part.vertices[0].x, part.vertices[0].y);
224 | //iterate through field of view slices to check collisions with target
225 | for(let slice of this.slicedIntersections) {
226 | let overlap = Phaser.Geom.Intersects.TriangleToLine(slice, segment);
227 |
228 | if(overlap) {
229 | return true;
230 | }
231 | }
232 | }
233 |
234 | return false;
235 | }
236 |
--------------------------------------------------------------------------------
/src/ray/range.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set ray's range.
3 | *
4 | * @method Raycaster.Ray#setRayRange
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {number} [rayRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
10 | *
11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
12 | */
13 | export function setRayRange(rayRange = Phaser.Math.MAX_SAFE_INTEGER) {
14 | this.rayRange = rayRange;
15 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
16 | return this;
17 | }
18 |
19 | /**
20 | * Set ray's maximum detection range. Objects outside detection range won't be tested.
21 | * Ray tests all objects when set to 0.
22 | *
23 | * @method Raycaster.Ray#setDetectionRange
24 | * @memberof Raycaster.Ray
25 | * @instance
26 | * @since 0.6.0
27 | *
28 | * @param {number} [detectionRange = 0] - Maximum distance between ray's position and tested objects bounding boxes.
29 | *
30 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
31 | */
32 | export function setDetectionRange(detectionRange = 0) {
33 | this.detectionRange = detectionRange;
34 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
35 |
36 | return this;
37 | }
38 |
39 | /**
40 | * Set ray's field of view maximum collision range. Objects outside collision range won't be tested by {@link Raycaster.Ray#overlap Raycaster.Ray.overlap} method.
41 | * Determines ray's physics body radius.
42 | *
43 | * @method Raycaster.Ray#setCollisionRange
44 | * @memberof Raycaster.Ray
45 | * @instance
46 | * @since 0.8.0
47 | *
48 | * @param {number} [collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's collision range and physics body radius.
49 | *
50 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
51 | */
52 | export function setCollisionRange(collisionRange = Phaser.Math.MAX_SAFE_INTEGER) {
53 | let oldRangeMax = this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER;
54 | this.collisionRange = collisionRange;
55 | this.collisionCircle.setRadius(this.collisionRange);
56 |
57 | if(this.bodyType === 'matter') {
58 | if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) {
59 | let bounds = this._raycaster.boundingBox;
60 |
61 | this._raycaster.scene.matter.body.set(this.body, {
62 | shape: {
63 | type: 'rectangle',
64 | x: bounds.rectangle.centerX,
65 | y: bounds.rectangle.centerY,
66 | width: bounds.rectangle.width,
67 | height: bounds.rectangle.height,
68 | circleRadius:0
69 | }
70 | });
71 | }
72 | else if(oldRangeMax) {
73 | this._raycaster.scene.matter.body.set(this.body, {
74 | shape: {
75 | type: 'circle',
76 | x: this.collisionCircle.x,
77 | y: this.collisionCircle.y
78 | },
79 | circleRadius: this.collisionRange,
80 | isStatic: false
81 | });
82 | }
83 | else {
84 | this.collisionCircle.setRadius(this.collisionRange);
85 | }
86 | this._raycaster.scene.matter.body.set(this.body, 'circleRadius', this.collisionRange)
87 | }
88 | else if(this.bodyType === 'arcade') {
89 | this.body.setCircle(this.collisionRange);
90 | }
91 |
92 | return this;
93 | }
94 |
95 | /**
96 | * Test if object's bounding box is in ray's detection range.
97 | *
98 | * @method Raycaster.Ray#boundsInRange
99 | * @memberof Raycaster.Ray
100 | * @instance
101 | * @since 0.6.0
102 | *
103 | * @param {object} object - Tested object
104 | * @param {(Phaser.Geom.Rectangle|boolean)} [bounds = false] - Tested object's bounds. If not passed bounds will be generated automatically.
105 | *
106 | * @return {boolean} Information if object is in ray's detection range.
107 | */
108 | export function boundsInRange(object, bounds = false) {
109 | if(!this.detectionRange)
110 | return true;
111 |
112 | let objectBounds;
113 | if(bounds)
114 | objectBounds = bounds;
115 | else {
116 | if(object.type === 'body' || object.type === 'composite')
117 | objectBounds = object.raycasterMap.getBoundingBox();
118 | else
119 | objectBounds = object.data.get('raycasterMap').getBoundingBox();
120 | }
121 |
122 | if(Phaser.Geom.Intersects.CircleToRectangle(this.detectionRangeCircle, objectBounds))
123 | return true;
124 |
125 | return false;
126 | }
127 |
--------------------------------------------------------------------------------
/src/ray/ray-core.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @classdesc
3 | *
4 | * Ray class responsible for casting ray's and testing their collisions with mapped objects.
5 | *
6 | * @namespace Raycaster.Ray
7 | * @class Raycaster.Ray
8 | * @constructor
9 | * @since 0.6.0
10 | *
11 | * @param {object} [options] - Ray's congfiguration options. May include:
12 | * @param {Phaser.Geom.Point|Point} [options.origin = {x:0, y:0}] - Ray's position.
13 | * @param {number} [options.angle = 0] - Ray's angle in radians.
14 | * @param {number} [options.angleDeg = 0] - Ray's angle in degrees.
15 | * @param {number} [options.cone = 0] - Ray's cone angle in radians.
16 | * @param {number} [options.coneDeg = 0] - Ray's cone angle in degrees.
17 | * @param {number} [options.range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
18 | * @param {number} [options.collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's maximum collision range of ray's field of view.
19 | * @param {number} [options.detectionRange = Phaser.Math.MAX_SAFE_INTEGER] - Maximum distance between ray's position and tested objects bounding boxes.
20 | * @param {boolean} [options.ignoreNotIntersectedRays = true] - If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position.
21 | * @param {boolean} [options.autoSlice = false] - If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}.
22 | * @param {boolean} [options.round = false] - If set true, point where ray hit will be rounded.
23 | * @param {(boolean|'arcade'|'matter')} [options.enablePhysics = false] - Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. If set true, arcade physics body will be added.
24 | * @param {Raycaster} [raycaster] - Parent raycaster object.
25 | */
26 | export function Ray(options, raycaster) {
27 | /**
28 | * Reference to parent Raycaster object.
29 | *
30 | * @name Raycaster.Ray#_raycaster
31 | * @type {Raycaster}
32 | * @private
33 | * @since 0.6.0
34 | */
35 | this._raycaster = raycaster ? raycaster : false;
36 | /**
37 | * Ray's source position.
38 | *
39 | * @name Raycaster.Ray#origin
40 | * @type {Phaser.Geom.Point}
41 | * @since 0.6.0
42 | */
43 | this.origin = new Phaser.Geom.Point();
44 | /**
45 | * Ray's representation used to calculating intersections.
46 | *
47 | * @name Raycaster.Ray#_ray
48 | * @type {Phaser.Geom.Line}
49 | * @private
50 | * @since 0.6.0
51 | */
52 | this._ray = new Phaser.Geom.Line();
53 | /**
54 | * Ray's angle in radians.
55 | *
56 | * @name Raycaster.Ray#angle
57 | * @type {number}
58 | * @default 0
59 | * @since 0.6.0
60 | */
61 | this.angle = 0;
62 | /**
63 | * Ray's cone width angle in radians.
64 | *
65 | * @name Raycaster.Ray#cone
66 | * @type {number}
67 | * @default 0
68 | * @since 0.7.0
69 | */
70 | this.cone = 0;
71 | /**
72 | * Ray's maximum range
73 | *
74 | * @name Raycaster.Ray#rayRange
75 | * @type {number}
76 | * @default Phaser.Math.MAX_SAFE_INTEGER
77 | * @since 0.6.0
78 | */
79 | this.rayRange = Phaser.Math.MAX_SAFE_INTEGER;
80 | /**
81 | * Ray's maximum detection range. Objects outside detection range won't be tested.
82 | * Ray tests all objects when set to 0.
83 | *
84 | * @name Raycaster.Ray#detectionRange
85 | * @type {number}
86 | * @default
87 | * @since 0.6.0
88 | */
89 | this.detectionRange = 0;
90 | /**
91 | * Ray's representation of detection range used in calculating if objects are in range.
92 | *
93 | * @name Raycaster.Ray#detectionRangeCircle
94 | * @type {Phaser.Geom.Circle}
95 | * @private
96 | * @since 0.6.0
97 | */
98 | this.detectionRangeCircle = new Phaser.Geom.Circle();
99 | /**
100 | * Ray's maximum collision range of ray's field of view. Radius of {@link Raycaster.Ray#collisionRangeCircle Ray.body}.
101 | *
102 | * @name Raycaster.Ray#collisionRange
103 | * @type {number}
104 | * @default Phaser.Math.MAX_SAFE_INTEGER
105 | * @since 0.8.0
106 | */
107 | this.collisionRange = Phaser.Math.MAX_SAFE_INTEGER;
108 | /**
109 | * If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position.
110 | *
111 | * @name Raycaster.Ray#ignoreNotIntersectedRays
112 | * @type {boolean}
113 | * @default true
114 | * @since 0.6.0
115 | */
116 | this.ignoreNotIntersectedRays = true;
117 | /**
118 | * If set true, ray's hit points will be rounded.
119 | *
120 | * @name Raycaster.Ray#round
121 | * @type {boolean}
122 | * @default false
123 | * @since 0.8.1
124 | */
125 | this.round = false;
126 | /**
127 | * If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}.
128 | *
129 | * @name Raycaster.Ray#autoSlice
130 | * @type {boolean}
131 | * @default false
132 | * @since 0.8.0
133 | */
134 | this.autoSlice = false;
135 | /**
136 | * Array of intersections from last raycast representing field of view.
137 | *
138 | * @name Raycaster.Ray#intersections
139 | * @type {object[]}
140 | * @default []
141 | * @since 0.8.0
142 | */
143 | this.intersections = [];
144 | /**
145 | * Array of triangles representing slices of field of view from last raycast.
146 | *
147 | * @name Raycaster.Ray#slicedIntersections
148 | * @type {Phaser.Geom.Triangle[]}
149 | * @default []
150 | * @since 0.8.0
151 | */
152 | this.slicedIntersections = [];
153 |
154 | /**
155 | * Physics body for testing field of view collisions.
156 | *
157 | * @name Raycaster.Ray#body
158 | * @type {object}
159 | * @default undefined
160 | * @since 0.8.0
161 | */
162 | //this.body = false;
163 | /**
164 | * Physics body type.
165 | *
166 | * @name Raycaster.Ray#bodyType
167 | * @type {(boolean|'arcade'|'matter')}
168 | * @default false
169 | * @since 0.9.0
170 | */
171 | this.bodyType = false;
172 |
173 | /**
174 | * Ray casting stats.
175 | *
176 | * @name Raycaster.Ray#_stats
177 | * @type {object}
178 | * @private
179 | * @since 0.10.0
180 | *
181 | * @property {string} method Used casting method (cast, castCircle, castCone).
182 | * @property {number} rays Casted rays.
183 | * @property {number} testedMappedObjects Tested mapped objects.
184 | * @property {number} hitMappedObjects Hit mapped objects.
185 | * @property {number} segments Tested segments.
186 | * @property {number} time Casting time.
187 | */
188 | this._stats = {
189 | method: 'cast',
190 | rays: 0,
191 | testedMappedObjects: 0,
192 | hitMappedObjects: 0,
193 | segments: 0,
194 | time: 0
195 | };
196 |
197 | /**
198 | * Ray's graphics object used for debug
199 | *
200 | * @name Raycaster.Ray#graphics
201 | * @type {Phaser.GameObjects.Graphics}
202 | * @private
203 | * @since 0.10.0
204 | */
205 | this.graphics;
206 |
207 | this.config(options);
208 | };
209 |
210 | Ray.prototype = {
211 | config: require('./config.js').config,
212 | getStats: require('./stats.js').getStats,
213 | setRay: require('./ray.js').setRay,
214 | setOrigin: require('./origin.js').setOrigin,
215 | setRayRange: require('./range.js').setRayRange,
216 | setAngle: require('./angle.js').setAngle,
217 | setAngleDeg: require('./angle.js').setAngleDeg,
218 | setCone: require('./cone.js').setCone,
219 | setConeDeg: require('./cone.js').setConeDeg,
220 | setDetectionRange: require('./range.js').setDetectionRange,
221 | boundsInRange: require('./range.js').boundsInRange,
222 | cast: require('./cast.js').cast,
223 | castCircle: require('./castCircle.js').castCircle,
224 | castCone: require('./castCone.js').castCone,
225 | slice: require('./slice.js').slice,
226 | setCollisionRange: require('./range.js').setCollisionRange,
227 | enablePhysics: require('./enablePhysics.js').enablePhysics,
228 | overlap: require('./overlap.js').overlap,
229 | processOverlap: require('./overlap.js').processOverlap,
230 | testArcadeOverlap: require('./overlap.js').testArcadeOverlap,
231 | testMatterOverlap: require('./overlap.js').testMatterOverlap,
232 | setCollisionCategory: require('./matter-physics-methods.js').setCollisionCategory,
233 | setCollisionGroup: require('./matter-physics-methods.js').setCollisionGroup,
234 | setCollidesWith: require('./matter-physics-methods.js').setCollidesWith,
235 | setOnCollide: require('./matter-physics-methods.js').setOnCollide,
236 | setOnCollideEnd: require('./matter-physics-methods.js').setOnCollideEnd,
237 | setOnCollideActive: require('./matter-physics-methods.js').setOnCollideActive,
238 | setOnCollideWith: require('./matter-physics-methods.js').setOnCollideWith,
239 | drawDebug: require('./debug.js').drawDebug,
240 | destroy: require('./destroy.js').destroy,
241 | };
242 |
--------------------------------------------------------------------------------
/src/ray/ray.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set ray's position, direction (angle) and range.
3 | *
4 | * @method Raycaster.Ray#setRay
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.6.0
8 | *
9 | * @param {number} x - X coordinate.
10 | * @param {number} y - Y coordinate.
11 | * @param {number} [angle] - Ray's angle in radians.
12 | * @param {number} [range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
13 | *
14 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
15 | */
16 | export function setRay(x, y, angle, rayRange = Phaser.Math.MAX_SAFE_INTEGER) {
17 | this.origin.setTo(x, y);
18 | this.angle = Phaser.Math.Angle.Normalize(angle);
19 | this.rayRange = rayRange;
20 |
21 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
22 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y, this.detectionRange);
23 | return this;
24 | }
25 |
--------------------------------------------------------------------------------
/src/ray/slice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Slice ray's field of view represented by polygon or array of points into array of triangles.
3 | *
4 | * @method Raycaster.Ray#slice
5 | * @memberof Raycaster.Ray
6 | * @instance
7 | * @since 0.8.0
8 | *
9 | * @param {(object[]|Phaser.Geom.Polygon)} [fov = {Ray#fov}] - Array of points or polygon representing field of view. If not passed, filed of view from last raycaste will be used.
10 | * @param {boolean} [closed = true|{Ray#fov}] - Define if field of view polygon is closed (first and last vertices sholud be connected). If fov was not passed, value depends of last type of casting.
11 | *
12 | * @return {Phaser.Geom.Triangle[]} Array of triangles representing slices of field of view.
13 | */
14 | export function slice(intersections = this.intersections, closed = true) {
15 | //if intersections is Phaser.Geom.Polygon object
16 | if(!Array.isArray(intersections)) {
17 | if(intersections.type === 4)
18 | intersections = intersections.points;
19 | else
20 | return [];
21 | }
22 |
23 | if(intersections.length === 0)
24 | return [];
25 |
26 | let slices = [];
27 | for(let i = 0, iLength = intersections.length - 1; i < iLength; i++) {
28 | slices.push(new Phaser.Geom.Triangle(this.origin.x, this.origin.y, intersections[i].x, intersections[i].y, intersections[i+1].x, intersections[i+1].y));
29 | }
30 |
31 | if(closed)
32 | slices.push(new Phaser.Geom.Triangle(this.origin.x, this.origin.y, intersections[0].x, intersections[0].y, intersections[intersections.length-1].x, intersections[intersections.length-1].y));
33 |
34 | return slices;
35 | }
36 |
--------------------------------------------------------------------------------
/src/ray/stats.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get ray statistics for last casting. Stats include
3 | * * number of casted rays,
4 | * * number of tested mapped objects,
5 | * * number of tested map segments.
6 | * * casting time
7 | *
8 | * @method Raycaster.Ray#getStats
9 | * @memberof Raycaster.Ray
10 | * @instance
11 | * @since 0.10.0
12 | *
13 | * @return {object} Statisticss from last casting.
14 | */
15 | export function getStats() {
16 | return this._stats;
17 | }
18 |
--------------------------------------------------------------------------------