├── docs
├── robots.txt
├── favicon.ico
├── media
│ ├── hit.f8648124.wav
│ ├── score.63ce9ecd.wav
│ ├── side.f37fcc5f.wav
│ └── start.1f4848ab.wav
├── img
│ └── icons
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── apple-touch-icon.png
│ │ ├── mstile-150x150.png
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon-60x60.png
│ │ ├── apple-touch-icon-76x76.png
│ │ ├── apple-touch-icon-120x120.png
│ │ ├── apple-touch-icon-152x152.png
│ │ ├── apple-touch-icon-180x180.png
│ │ ├── msapplication-icon-144x144.png
│ │ ├── android-chrome-maskable-192x192.png
│ │ ├── android-chrome-maskable-512x512.png
│ │ └── safari-pinned-tab.svg
├── css
│ └── app.4dedb158.css
├── js
│ ├── about.c058c680.js
│ ├── about.c058c680.js.map
│ ├── app.4b46cf16.js
│ └── app.4b46cf16.js.map
├── manifest.json
├── 404.html
├── service-worker.js
├── precache-manifest.94bcd2149b8874d0d73d930b3affd641.js
└── index.html
├── public
├── robots.txt
├── favicon.ico
├── img
│ └── icons
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── mstile-150x150.png
│ │ ├── apple-touch-icon.png
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon-120x120.png
│ │ ├── apple-touch-icon-152x152.png
│ │ ├── apple-touch-icon-180x180.png
│ │ ├── apple-touch-icon-60x60.png
│ │ ├── apple-touch-icon-76x76.png
│ │ ├── msapplication-icon-144x144.png
│ │ ├── android-chrome-maskable-192x192.png
│ │ ├── android-chrome-maskable-512x512.png
│ │ └── safari-pinned-tab.svg
├── 404.html
└── index.html
├── .browserslistrc
├── cypress.json
├── jest.config.js
├── src
├── assets
│ ├── hit.wav
│ ├── logo.png
│ ├── score.wav
│ ├── side.wav
│ └── start.wav
├── pong
│ ├── movement.js
│ ├── index.js
│ ├── shaders
│ │ ├── shader.vert
│ │ └── shader.frag
│ ├── player.js
│ ├── ball.js
│ ├── shader.js
│ ├── pong-renderer.js
│ └── pong.js
├── views
│ ├── About.vue
│ └── Home.vue
├── store
│ └── index.js
├── main.js
├── App.vue
├── router
│ └── index.js
├── registerServiceWorker.js
└── components
│ └── HelloWorld.vue
├── babel.config.js
├── Instructions
└── INFOGR_P3_2020_Retake.pdf
├── .editorconfig
├── tests
├── e2e
│ ├── .eslintrc.js
│ ├── specs
│ │ └── test.js
│ ├── support
│ │ ├── index.js
│ │ └── commands.js
│ └── plugins
│ │ └── index.js
└── unit
│ └── example.spec.js
├── vue.config.js
├── .gitignore
├── .eslintrc.js
├── package.json
├── README.md
└── README.txt
/docs/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "pluginsFile": "tests/e2e/plugins/index.js"
3 | }
4 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/favicon.ico
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: '@vue/cli-plugin-unit-jest'
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/favicon.ico
--------------------------------------------------------------------------------
/src/assets/hit.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/src/assets/hit.wav
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/score.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/src/assets/score.wav
--------------------------------------------------------------------------------
/src/assets/side.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/src/assets/side.wav
--------------------------------------------------------------------------------
/src/assets/start.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/src/assets/start.wav
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/docs/media/hit.f8648124.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/media/hit.f8648124.wav
--------------------------------------------------------------------------------
/docs/media/score.63ce9ecd.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/media/score.63ce9ecd.wav
--------------------------------------------------------------------------------
/docs/media/side.f37fcc5f.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/media/side.f37fcc5f.wav
--------------------------------------------------------------------------------
/docs/media/start.1f4848ab.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/media/start.1f4848ab.wav
--------------------------------------------------------------------------------
/docs/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/pong/movement.js:
--------------------------------------------------------------------------------
1 | const Movement = {
2 | None: 0,
3 | Up: 1,
4 | Down: 2
5 | }
6 |
7 | export default Movement
8 |
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Instructions/INFOGR_P3_2020_Retake.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/Instructions/INFOGR_P3_2020_Retake.pdf
--------------------------------------------------------------------------------
/docs/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/docs/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/docs/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/public/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/docs/img/icons/android-chrome-maskable-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/android-chrome-maskable-192x192.png
--------------------------------------------------------------------------------
/docs/img/icons/android-chrome-maskable-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/docs/img/icons/android-chrome-maskable-512x512.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-maskable-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/android-chrome-maskable-192x192.png
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-maskable-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-pong/master/public/img/icons/android-chrome-maskable-512x512.png
--------------------------------------------------------------------------------
/src/pong/index.js:
--------------------------------------------------------------------------------
1 | import { Pong, GameStage } from './pong'
2 | import PongRenderer from './pong-renderer'
3 |
4 | export { Pong, PongRenderer, GameStage }
5 |
--------------------------------------------------------------------------------
/tests/e2e/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | 'cypress'
4 | ],
5 | env: {
6 | mocha: true,
7 | 'cypress/globals': true
8 | },
9 | rules: {
10 | strict: 'off'
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/docs/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex)
5 |
6 | export default new Vuex.Store({
7 | state: {
8 | },
9 | mutations: {
10 | },
11 | actions: {
12 | },
13 | modules: {
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/tests/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // https://docs.cypress.io/api/introduction/api.html
2 |
3 | describe('My First Test', () => {
4 | it('Visits the app root url', () => {
5 | cy.visit('/')
6 | cy.contains('h1', 'Welcome to Your Vue.js App')
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/public/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import './registerServiceWorker'
4 | import router from './router'
5 | import store from './store'
6 |
7 | Vue.config.productionTip = false
8 |
9 | new Vue({
10 | router,
11 | store,
12 | render: h => h(App)
13 | }).$mount('#app')
14 |
--------------------------------------------------------------------------------
/docs/css/app.4dedb158.css:
--------------------------------------------------------------------------------
1 | body{background-color:#000}#app,body{padding:0;margin:0}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;height:100vh;width:100vw}#app,.home{display:grid}.home{align-items:center;justify-content:center}canvas{background-color:#fff;margin:0;padding:0}
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | publicPath: '/infogr-pong/',
5 | outputDir: path.resolve(__dirname, './docs'),
6 | configureWebpack: {
7 | module: {
8 | rules: [
9 | {
10 | test: /\.vert$|\.frag$/i,
11 | use: 'raw-loader'
12 | }
13 | ]
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/pong/shaders/shader.vert:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | attribute vec4 aVertexPosition;
4 |
5 | uniform mat4 uModelViewMatrix;
6 | uniform mat4 uProjectionMatrix;
7 |
8 | varying mediump vec2 screenPosition;
9 |
10 | void main() {
11 | screenPosition = aVertexPosition.xy;
12 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | /tests/e2e/videos/
6 | /tests/e2e/screenshots/
7 |
8 | # local env files
9 | .env.local
10 | .env.*.local
11 |
12 | # Log files
13 | npm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 | pnpm-debug.log*
17 |
18 | # Editor directories and files
19 | .idea
20 | .vscode
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/tests/unit/example.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import HelloWorld from '@/components/HelloWorld.vue'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(HelloWorld, {
8 | propsData: { msg }
9 | })
10 | expect(wrapper.text()).toMatch(msg)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/docs/js/about.c058c680.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"about"},[n("h1",[t._v("This is an about page")])])}],u=n("2877"),c={},i=Object(u["a"])(c,a,s,!1,null,null,null);e["default"]=i.exports}}]);
2 | //# sourceMappingURL=about.c058c680.js.map
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
--------------------------------------------------------------------------------
/docs/manifest.json:
--------------------------------------------------------------------------------
1 | {"name":"infogr-pong","short_name":"infogr-pong","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"}
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | INFOGR Pong
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | INFOGR Pong
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: [
7 | 'plugin:vue/essential',
8 | '@vue/standard'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
16 | },
17 | overrides: [
18 | {
19 | files: [
20 | '**/__tests__/*.{j,t}s?(x)',
21 | '**/tests/unit/**/*.spec.{j,t}s?(x)'
22 | ],
23 | env: {
24 | jest: true
25 | }
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/src/pong/player.js:
--------------------------------------------------------------------------------
1 | import Movement from './movement'
2 |
3 | export default class Player {
4 | width
5 | height
6 | x
7 | y
8 | direction
9 | score
10 | speed
11 |
12 | constructor (x) {
13 | this.width = 0.04
14 | this.height = 0.3
15 | this.x = x
16 | this.y = 0
17 | this.speed = 0.02
18 | this.direction = Movement.None
19 | this.score = 0
20 | }
21 |
22 | left () {
23 | return this.x - this.width / 2
24 | }
25 |
26 | right () {
27 | return this.x + this.width / 2
28 | }
29 |
30 | top () {
31 | return this.y + this.height / 2
32 | }
33 |
34 | bottom () {
35 | return this.y - this.height / 2
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import Home from '../views/Home.vue'
4 |
5 | Vue.use(VueRouter)
6 |
7 | const routes = [
8 | {
9 | path: '/',
10 | name: 'Home',
11 | component: Home
12 | },
13 | {
14 | path: '/about',
15 | name: 'About',
16 | // route level code-splitting
17 | // this generates a separate chunk (about.[hash].js) for this route
18 | // which is lazy-loaded when the route is visited.
19 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
20 | }
21 | ]
22 |
23 | const router = new VueRouter({
24 | mode: 'history',
25 | base: process.env.BASE_URL,
26 | routes
27 | })
28 |
29 | export default router
30 |
--------------------------------------------------------------------------------
/tests/e2e/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/tests/e2e/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/e2e/plugins/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable arrow-body-style */
2 | // https://docs.cypress.io/guides/guides/plugins-guide.html
3 |
4 | // if you need a custom webpack configuration you can uncomment the following import
5 | // and then use the `file:preprocessor` event
6 | // as explained in the cypress docs
7 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
8 |
9 | // /* eslint-disable import/no-extraneous-dependencies, global-require */
10 | // const webpack = require('@cypress/webpack-preprocessor')
11 |
12 | module.exports = (on, config) => {
13 | // on('file:preprocessor', webpack({
14 | // webpackOptions: require('@vue/cli-service/webpack.config'),
15 | // watchOptions: {}
16 | // }))
17 |
18 | return Object.assign({}, config, {
19 | fixturesFolder: 'tests/e2e/fixtures',
20 | integrationFolder: 'tests/e2e/specs',
21 | screenshotsFolder: 'tests/e2e/screenshots',
22 | videosFolder: 'tests/e2e/videos',
23 | supportFile: 'tests/e2e/support/index.js'
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | import { register } from 'register-service-worker'
4 |
5 | if (process.env.NODE_ENV === 'production') {
6 | register(`${process.env.BASE_URL}service-worker.js`, {
7 | ready () {
8 | console.log(
9 | 'App is being served from cache by a service worker.\n' +
10 | 'For more details, visit https://goo.gl/AFskqB'
11 | )
12 | },
13 | registered () {
14 | console.log('Service worker has been registered.')
15 | },
16 | cached () {
17 | console.log('Content has been cached for offline use.')
18 | },
19 | updatefound () {
20 | console.log('New content is downloading.')
21 | },
22 | updated () {
23 | console.log('New content is available; please refresh.')
24 | },
25 | offline () {
26 | console.log('No internet connection found. App is running in offline mode.')
27 | },
28 | error (error) {
29 | console.error('Error during service worker registration:', error)
30 | }
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/src/pong/ball.js:
--------------------------------------------------------------------------------
1 | export default class Ball {
2 | radius
3 | x
4 | y
5 | direction
6 | oldPositions = []
7 | speed
8 | acceleration
9 |
10 | startDirections = [
11 | { x: 0.01, y: 0.01 },
12 | { x: 0.01, y: -0.01 },
13 | { x: -0.01, y: 0.01 },
14 | { x: -0.01, y: -0.01 }
15 | ]
16 |
17 | setLocation (x, y) {
18 | this.oldPositions.push(new Float32Array([x, y]))
19 | if (this.oldPositions.length > 30) {
20 | this.oldPositions.shift()
21 | }
22 | this.x = x
23 | this.y = y
24 | }
25 |
26 | constructor () {
27 | this.radius = 0.01
28 | this.x = 0
29 | this.y = 0
30 | this.direction = this.startDirections[Math.floor(Math.random() * this.startDirections.length)]
31 | this.speed = 0.01
32 | this.acceleration = 1.1
33 |
34 | for (var i = 0; i < 30; i++) {
35 | this.oldPositions[i] = new Float32Array([0, 0])
36 | }
37 | }
38 |
39 | left () {
40 | return this.x - this.radius
41 | }
42 |
43 | right () {
44 | return this.x + this.radius
45 | }
46 |
47 | top () {
48 | return this.y + this.radius
49 | }
50 |
51 | bottom () {
52 | return this.y - this.radius
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "/infogr-pong/precache-manifest.94bcd2149b8874d0d73d930b3affd641.js"
18 | );
19 |
20 | workbox.core.setCacheNameDetails({prefix: "infogr-pong"});
21 |
22 | self.addEventListener('message', (event) => {
23 | if (event.data && event.data.type === 'SKIP_WAITING') {
24 | self.skipWaiting();
25 | }
26 | });
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
--------------------------------------------------------------------------------
/docs/js/about.c058c680.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///./src/views/About.vue?cdaf","webpack:///./src/views/About.vue"],"names":["render","_vm","this","_h","$createElement","_self","_c","_m","staticRenderFns","staticClass","_v","script","component"],"mappings":"8GAAA,IAAIA,EAAS,WAAa,IAAIC,EAAIC,KAASC,EAAGF,EAAIG,eAAsBH,EAAII,MAAMC,GAAO,OAAOL,EAAIM,GAAG,IACnGC,EAAkB,CAAC,WAAa,IAAIP,EAAIC,KAASC,EAAGF,EAAIG,eAAmBE,EAAGL,EAAII,MAAMC,IAAIH,EAAG,OAAOG,EAAG,MAAM,CAACG,YAAY,SAAS,CAACH,EAAG,KAAK,CAACL,EAAIS,GAAG,+B,YCAtJC,EAAS,GAKTC,EAAY,eACdD,EACAX,EACAQ,GACA,EACA,KACA,KACA,MAIa,aAAAI,E","file":"js/about.c058c680.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"about\"},[_c('h1',[_vm._v(\"This is an about page\")])])}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./About.vue?vue&type=template&id=1ae8a7be&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
41 |
42 |
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "infogr-pong",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "test:unit": "vue-cli-service test:unit",
9 | "test:e2e": "vue-cli-service test:e2e",
10 | "lint": "vue-cli-service lint"
11 | },
12 | "dependencies": {
13 | "core-js": "^3.6.5",
14 | "register-service-worker": "^1.7.1",
15 | "vue": "^2.6.11",
16 | "vue-router": "^3.2.0",
17 | "vuex": "^3.4.0"
18 | },
19 | "devDependencies": {
20 | "@vue/cli-plugin-babel": "~4.4.0",
21 | "@vue/cli-plugin-e2e-cypress": "~4.4.0",
22 | "@vue/cli-plugin-eslint": "~4.4.0",
23 | "@vue/cli-plugin-pwa": "~4.4.0",
24 | "@vue/cli-plugin-router": "~4.4.0",
25 | "@vue/cli-plugin-unit-jest": "~4.4.0",
26 | "@vue/cli-plugin-vuex": "~4.4.0",
27 | "@vue/cli-service": "~4.4.0",
28 | "@vue/eslint-config-standard": "^5.1.2",
29 | "@vue/test-utils": "^1.0.3",
30 | "babel-eslint": "^10.1.0",
31 | "eslint": "^6.7.2",
32 | "eslint-plugin-import": "^2.20.2",
33 | "eslint-plugin-node": "^11.1.0",
34 | "eslint-plugin-promise": "^4.2.1",
35 | "eslint-plugin-standard": "^4.0.0",
36 | "eslint-plugin-vue": "^6.2.2",
37 | "gl-matrix": "^3.3.0",
38 | "raw-loader": "^4.0.1",
39 | "sass": "^1.26.5",
40 | "sass-loader": "^8.0.2",
41 | "vue-template-compiler": "^2.6.11"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/docs/precache-manifest.94bcd2149b8874d0d73d930b3affd641.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "d5085e11daa4d890913fbdb319257c7e",
4 | "url": "/infogr-pong/404.html"
5 | },
6 | {
7 | "revision": "f04aff55e9b4797b9f90",
8 | "url": "/infogr-pong/css/app.4dedb158.css"
9 | },
10 | {
11 | "revision": "1b685c29570444d1772f80ff36fbaade",
12 | "url": "/infogr-pong/index.html"
13 | },
14 | {
15 | "revision": "792387564e323932678d",
16 | "url": "/infogr-pong/js/about.c058c680.js"
17 | },
18 | {
19 | "revision": "f04aff55e9b4797b9f90",
20 | "url": "/infogr-pong/js/app.4b46cf16.js"
21 | },
22 | {
23 | "revision": "af44d2a93ec7d451b576",
24 | "url": "/infogr-pong/js/chunk-vendors.7d9f1d29.js"
25 | },
26 | {
27 | "revision": "124e9e43b4163fb40b51b6be966f8d17",
28 | "url": "/infogr-pong/manifest.json"
29 | },
30 | {
31 | "revision": "f8648124be0a914e9984facdf89dcbfa",
32 | "url": "/infogr-pong/media/hit.f8648124.wav"
33 | },
34 | {
35 | "revision": "63ce9ecdb52b2896bd16bd8d26b07e36",
36 | "url": "/infogr-pong/media/score.63ce9ecd.wav"
37 | },
38 | {
39 | "revision": "f37fcc5fe035b88645f0097d4ca6fcd6",
40 | "url": "/infogr-pong/media/side.f37fcc5f.wav"
41 | },
42 | {
43 | "revision": "1f4848ab75563b62b1e273c319c8124c",
44 | "url": "/infogr-pong/media/start.1f4848ab.wav"
45 | },
46 | {
47 | "revision": "b6216d61c03e6ce0c9aea6ca7808f7ca",
48 | "url": "/infogr-pong/robots.txt"
49 | }
50 | ]);
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | infogr-pong
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # infogr-pong
2 |
3 | Authors:
4 | - Cor Pruijs (6595154)
5 | - Karel Kubat (6913466)
6 |
7 | ## Work division
8 |
9 | We pair programmed the entire application together. Cor did produce the actual sound files, and
10 | can be considered our lead designer.
11 |
12 | ## Features
13 |
14 | - Pong
15 | - Scoring
16 | - Character rendering
17 | - Score rendering
18 | - Ball tail
19 | - Randomized start angle
20 | - Ball speed up
21 | - Interactive play button (pointer events)
22 | - Start screen / Win screen
23 | - WebGL
24 | - VueJS app + hosting
25 | - VHS effect
26 | - Sound effects
27 | - Screen curvature
28 |
29 | ## Running the game
30 | Our entire game can be viewed [online](https://cor.github.io/infogr-pong)!
31 | Please view it in an updated version of Google Chrome or FireFox.
32 |
33 | ### Controls
34 | - Click the triangular play button to start
35 | - W/S for Player 1 movement
36 | - UpArrow/DownArrow for Player 2 movement
37 |
38 | ## Sources
39 |
40 | StackOverflow sources are included in the actual code. We used snippets to determine mouse locations on the
41 | HTML canvas, and [wessles](https://github.com/wessles/GLSL-CRT/blob/master/shader.frag) implementation of screen curvatures.
42 |
43 | The algorithm used to determine the angle that the ball makes when hitting the paddle is sourced from [Cor's previous
44 | Pong implementation](https://github.com/cor/Ping)
45 |
46 | Finally, we used official OpenGL and WebGL tutorials to create the app,
47 | and previous work by us on the [raytracer assignment](https://github.com/cor/infgr-ratyracer) as well.
48 |
49 | ## Project setup
50 |
51 | This project requires a modern JavaScript environment, most likely [npm](https://www.npmjs.com/) and [node](https://nodejs.org/en/).
52 |
53 | ```
54 | npm install
55 | ```
56 |
57 | ### Compiles and hot-reloads for development
58 | ```
59 | npm run serve
60 | ```
61 |
62 | ### Compiles and minifies for production
63 | ```
64 | npm run build
65 | ```
66 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | # infogr-pong
2 |
3 | Authors:
4 | - Cor Pruijs (6595154)
5 | - Karel Kubat (6913466)
6 |
7 | ## Work division
8 |
9 | We pair programmed the entire application together. Cor did produce the actual sound files, and
10 | can be considered our lead designer.
11 |
12 | ## Features
13 |
14 | - Pong
15 | - Scoring
16 | - Character rendering
17 | - Score rendering
18 | - Ball tail
19 | - Randomized start angle
20 | - Ball speed up
21 | - Interactive play button (pointer events)
22 | - Start screen / Win screen
23 | - WebGL
24 | - VueJS app + hosting
25 | - VHS effect
26 | - Sound effects
27 | - Screen curvature
28 |
29 | ## Running the game
30 | Our entire game can be viewed [online](https://cor.github.io/infogr-pong)!
31 | Please view it in an updated version of Google Chrome or FireFox.
32 |
33 | ### Controls
34 | - Click the triangular play button to start
35 | - W/S for Player 1 movement
36 | - UpArrow/DownArrow for Player 2 movement
37 |
38 | ## Sources
39 |
40 | StackOverflow sources are included in the actual code. We used snippets to determine mouse locations on the
41 | HTML canvas, and [wessles](https://github.com/wessles/GLSL-CRT/blob/master/shader.frag) implementation of screen curvatures.
42 |
43 | The algorithm used to determine the angle that the ball makes when hitting the paddle is sourced from [Cor's previous
44 | Pong implementation](https://github.com/cor/Ping)
45 |
46 | Finally, we used official OpenGL and WebGL tutorials to create the app,
47 | and previous work by us on the [raytracer assignment](https://github.com/cor/infgr-ratyracer) as well.
48 |
49 | ## Project setup
50 |
51 | This project requires a modern JavaScript environment, most likely [npm](https://www.npmjs.com/) and [node](https://nodejs.org/en/).
52 |
53 | ```
54 | npm install
55 | ```
56 |
57 | ### Compiles and hot-reloads for development
58 | ```
59 | npm run serve
60 | ```
61 |
62 | ### Compiles and minifies for production
63 | ```
64 | npm run build
65 | ```
66 |
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |
9 |
Installed CLI Plugins
10 |
19 |
Essential Links
20 |
27 |
Ecosystem
28 |
35 |
36 |
37 |
38 |
46 |
47 |
48 |
64 |
--------------------------------------------------------------------------------
/src/pong/shader.js:
--------------------------------------------------------------------------------
1 | export default class Shader {
2 | gl
3 | program
4 |
5 | // constructor (gl, vsSource, fsSource, sourceVars) {
6 | constructor (gl, vsSource, fsSource) {
7 | this.gl = gl
8 |
9 | // preprocess sources
10 | // vsSource = this.preprocessSource(vsSource, sourceVars)
11 | // fsSource = this.preprocessSource(fsSource, sourceVars)
12 |
13 | this.program = this.initShaderProgram(gl, vsSource, fsSource)
14 | }
15 |
16 | //
17 | // Initialize a shader program, so WebGL knows how to draw our data
18 | //
19 | initShaderProgram (gl, vsSource, fsSource) {
20 | const vertexShader = this.loadShader(gl, gl.VERTEX_SHADER, vsSource)
21 | const fragmentShader = this.loadShader(gl, gl.FRAGMENT_SHADER, fsSource)
22 |
23 | // Create the shader program
24 | const shaderProgram = gl.createProgram()
25 | gl.attachShader(shaderProgram, vertexShader)
26 | gl.attachShader(shaderProgram, fragmentShader)
27 | gl.linkProgram(shaderProgram)
28 |
29 | // If creating the shader program failed, alert
30 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
31 | alert(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`)
32 | return null
33 | }
34 |
35 | return shaderProgram
36 | }
37 |
38 | //
39 | // preprocesses our shader source files
40 | // it replaces variables in the format 42//$VARIABLE_NAME$// with the VARIABLE_NAME
41 | // item from the sourceVars object
42 | //
43 | preprocessSource (source, sourceVars) {
44 | const sourceVarPattern = /42\/\/\$(.*)\$\/\//g
45 | return source.replace(sourceVarPattern, (_, varName) => sourceVars[varName])
46 | }
47 |
48 | //
49 | // creates a shader of the given type, uploads the source and
50 | // compiles it.
51 | //
52 | loadShader (gl, type, source) {
53 | const shader = gl.createShader(type)
54 |
55 | // Send the source to the shader object
56 | gl.shaderSource(shader, source)
57 |
58 | // Compile the shader program
59 | gl.compileShader(shader)
60 |
61 | // See if it compiled successfully
62 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
63 | console.error(`An error occurred compiling the shaders:\n${gl.getShaderInfoLog(shader)}`)
64 | gl.deleteShader(shader)
65 | return null
66 | }
67 |
68 | return shader
69 | }
70 |
71 | use () {
72 | this.gl.useProgram(this.program)
73 | }
74 |
75 | setUniform1f (name, value) {
76 | this.gl.uniform1f(this.getUniformLocation(name), value)
77 | }
78 |
79 | setUniformMatrix4fv (name, value) {
80 | this.gl.uniformMatrix4fv(this.getUniformLocation(name), false, value)
81 | }
82 |
83 | setUniform2fv (name, value) {
84 | this.gl.uniform2fv(this.getUniformLocation(name), value)
85 | }
86 |
87 | setUniform3fv (name, value) {
88 | this.gl.uniform3fv(this.getUniformLocation(name), value)
89 | }
90 |
91 | getUniformLocation (name) {
92 | // TODO: Cache uniform locations
93 | return this.gl.getUniformLocation(this.program, name)
94 | }
95 |
96 | getAttribLocation (name) {
97 | // TODO: Cache attrib locations
98 | return this.gl.getAttribLocation(this.program, name)
99 | }
100 |
101 | delete () {
102 | this.gl.deleteProgram(this.program)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/pong/pong-renderer.js:
--------------------------------------------------------------------------------
1 | import { mat4 } from 'gl-matrix'
2 | import Shader from './shader'
3 |
4 | import fsSource from './shaders/shader.frag'
5 | import vsSource from './shaders/shader.vert'
6 |
7 | export default class PongRenderer {
8 | gl
9 | shader
10 | buffers
11 |
12 | // constructor (gl, shaderSourceVars) {
13 | constructor (gl) {
14 | this.gl = gl
15 | this.shader = new Shader(gl, vsSource, fsSource)
16 | this.buffers = this.initBuffers()
17 |
18 | this.shader.use()
19 | this.setDefaultPositions()
20 | }
21 |
22 | recompileShader (shaderSourceVars) {
23 | this.shader.delete()
24 | this.shader = new Shader(this.gl, vsSource, fsSource, shaderSourceVars)
25 | }
26 |
27 | initBuffers () {
28 | // Create a buffer for the square's positions.
29 |
30 | const positionBuffer = this.gl.createBuffer()
31 |
32 | // Select the positionBuffer as the one to apply buffer
33 | // operations to from here out.
34 |
35 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer)
36 |
37 | // Now create an array of positions for the square.
38 |
39 | const positions = [
40 | -1.0, 1.0,
41 | 1.0, 1.0,
42 | -1.0, -1.0,
43 | 1.0, -1.0
44 | ]
45 |
46 | // Now pass the list of positions into WebGL to build the
47 | // shape. We do this by creating a Float32Array from the
48 | // JavaScript array, then use it to fill the current buffer.
49 | this.gl.bufferData(this.gl.ARRAY_BUFFER,
50 | new Float32Array(positions),
51 | this.gl.STATIC_DRAW)
52 |
53 | return {
54 | position: positionBuffer
55 | }
56 | }
57 |
58 | draw () {
59 | this.gl.clearColor(0.0, 0.0, 0.0, 1.0) // Clear to black, fully opaque
60 | this.gl.clear(this.gl.COLOR_BUFFER_BIT)
61 | this.gl.clearDepth(1.0) // Clear everything
62 | this.gl.enable(this.gl.DEPTH_TEST) // Enable depth testing
63 | this.gl.depthFunc(this.gl.LEQUAL) // Near things obscure far things
64 |
65 | // Clear the canvas before we start drawing on it.
66 |
67 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
68 |
69 | // Create a perspective matrix, a special matrix that is
70 | // used to simulate the distortion of perspective in a camera.
71 | const fieldOfView = 29.8 * Math.PI / 180 // in radians
72 | const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight
73 | const zNear = 0.1
74 | const zFar = 100.0
75 | const projectionMatrix = mat4.create()
76 |
77 | // gl-matrix.js always has the first argument as the destination to receive the result.
78 | mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar)
79 |
80 | // Set the drawing position to the "identity" point, which is
81 | // the center of the scene.
82 | const modelViewMatrix = mat4.create()
83 |
84 | // Now move the drawing position a bit to where we want to
85 | // start drawing the square.
86 | mat4.translate(modelViewMatrix, // destination matrix
87 | modelViewMatrix, // matrix to translate
88 | [0.0, 0.0, -2.5]) // amount to translate
89 |
90 | // Tell WebGL how to pull out the positions from the position
91 | // buffer into the vertexPosition attribute.
92 | const vertexPositionLocation = this.shader.getAttribLocation('aVertexPosition')
93 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.position)
94 | this.gl.vertexAttribPointer(vertexPositionLocation, 2, this.gl.FLOAT, false, 0, 0)
95 | this.gl.enableVertexAttribArray(vertexPositionLocation)
96 |
97 | // Tell WebGL to use our program when drawing
98 | this.shader.use()
99 |
100 | // Set the shader uniforms
101 | this.shader.setUniformMatrix4fv('uProjectionMatrix', projectionMatrix)
102 | this.shader.setUniformMatrix4fv('uModelViewMatrix', modelViewMatrix)
103 | {
104 | const offset = 0
105 | const vertexCount = 4
106 | this.gl.drawArrays(this.gl.TRIANGLE_STRIP, offset, vertexCount)
107 | }
108 | }
109 |
110 | setOldPositions (oldPositions) {
111 | for (const [i, pos] of oldPositions.entries()) {
112 | this.shader.setUniform2fv(`oldPositions[${i}]`, pos)
113 | }
114 | }
115 |
116 | setTime (time) {
117 | this.shader.setUniform1f('time', time)
118 | }
119 |
120 | setGameStage (gameStage) {
121 | this.shader.setUniform1f('gameStage', gameStage)
122 | }
123 |
124 | setDefaultPositions () {
125 | this.setLeftPaddlePosition(-0.9, 0)
126 | this.setRightPaddlePosition(0.9, 0)
127 | this.setBallPosition(0, 0)
128 | }
129 |
130 | setLeftPaddlePosition (x, y) {
131 | this.shader.setUniform2fv('leftPaddlePosition', new Float32Array([x, y]))
132 | }
133 |
134 | setRightPaddlePosition (x, y) {
135 | this.shader.setUniform2fv('rightPaddlePosition', new Float32Array([x, y]))
136 | }
137 |
138 | setBallPosition (x, y) {
139 | this.shader.setUniform2fv('ballPosition', new Float32Array([x, y]))
140 | }
141 |
142 | setScore (x, y) {
143 | this.shader.setUniform2fv('score', new Float32Array([x, y]))
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/pong/pong.js:
--------------------------------------------------------------------------------
1 | import Player from './player'
2 | import Movement from './movement'
3 | import Ball from './ball'
4 |
5 | const ScoreAudio = new Audio(require('../assets/score.wav'))
6 | const PlopAudio = new Audio(require('../assets/hit.wav'))
7 | const StartAudio = new Audio(require('../assets/start.wav'))
8 | const SideAudio = new Audio(require('../assets/side.wav'))
9 |
10 | export const GameStage = {
11 | Welcome: 0.0,
12 | Playing: 1.0,
13 | P1Win: 2.0,
14 | P2Win: 3.0
15 | }
16 |
17 | export class Pong {
18 | countdown
19 | stage
20 | canvas
21 | center
22 | renderer
23 | state
24 | maxY
25 | time
26 |
27 | constructor (renderer, canvas, stage) {
28 | this.countdown = 60
29 | this.time = 0
30 | if (typeof stage !== 'undefined') {
31 | this.stage = stage
32 | } else {
33 | this.stage = GameStage.Playing
34 | }
35 |
36 | this.canvas = canvas
37 | this.renderer = renderer
38 | this.maxY = 0.65
39 | this.state = new State()
40 |
41 | this.center = {
42 | x: canvas.width / 2,
43 | y: canvas.height / 2
44 | }
45 | }
46 |
47 | tick () {
48 | this.time++
49 | this.renderer.setTime(this.time)
50 | if (this.stage === GameStage.Playing) {
51 | this.state = this.transition(this.state)
52 | this.syncState()
53 | } else {
54 | this.renderer.setGameStage(this.stage)
55 | }
56 | this.draw()
57 | }
58 |
59 | transition (state) {
60 | this.countdown--
61 | this.stage = this.state.currentStage()
62 | this.updatePlayerState(state.P1)
63 | this.updatePlayerState(state.P2)
64 |
65 | if (this.countdown > 0) {
66 | return state
67 | }
68 |
69 | if (this.countdown === 0) {
70 | StartAudio.play()
71 | }
72 |
73 | this.updateBallState(state.ball)
74 |
75 | if (state.ball.x <= -1) {
76 | const newState = new State()
77 | newState.P1 = state.P1
78 | newState.P2 = state.P2
79 | newState.P2.score++
80 | ScoreAudio.play()
81 | this.countdown = 60
82 | return newState
83 | } else if (state.ball.x >= 1) {
84 | const newState = new State()
85 | newState.P1 = state.P1
86 | newState.P2 = state.P2
87 | newState.P1.score++
88 | this.countdown = 60
89 | ScoreAudio.play()
90 | return newState
91 | }
92 |
93 | if (state.terminating) {
94 | return state
95 | }
96 |
97 | const maxBounceAngle = 5 * Math.PI / 12
98 |
99 | // Paddle collision
100 | if (state.ball.left() < state.P1.right()) {
101 | if ((state.ball.bottom() < state.P1.top() && state.ball.bottom > state.P1.bottom()) ||
102 | (state.ball.top() < state.P1.top() && state.ball.top() > state.P1.bottom())) {
103 | PlopAudio.play()
104 | const distanceToNormal = state.P1.y - state.ball.y
105 | const normalizedDistanceToNormal = distanceToNormal / (state.P1.height / 2)
106 | const bounceAngle = normalizedDistanceToNormal * maxBounceAngle
107 |
108 | state.ball.speed *= state.ball.acceleration
109 | state.ball.direction.x = state.ball.speed * Math.cos(bounceAngle)
110 | state.ball.direction.y = -state.ball.speed * Math.sin(bounceAngle)
111 | } else {
112 | state.terminating = true
113 | }
114 | }
115 |
116 | if (state.ball.right() > state.P2.left()) {
117 | if ((state.ball.bottom() < state.P2.top() && state.ball.bottom > state.P2.bottom()) ||
118 | (state.ball.top() < state.P2.top() && state.ball.top() > state.P2.bottom())) {
119 | PlopAudio.play()
120 | const distanceToNormal = state.P2.y - state.ball.y
121 | const normalizedDistanceToNormal = distanceToNormal / (state.P2.height / 2)
122 | const bounceAngle = normalizedDistanceToNormal * maxBounceAngle
123 |
124 | state.ball.speed *= state.ball.acceleration
125 |
126 | state.ball.direction.x = state.ball.speed * -Math.cos(bounceAngle)
127 | state.ball.direction.y = state.ball.speed * -Math.sin(bounceAngle)
128 | } else {
129 | state.terminating = true
130 | }
131 | }
132 |
133 | // if ball hits paddle, compute new ball direction.
134 | return this.state
135 | }
136 |
137 | updateBallState (ball) {
138 | ball.setLocation(ball.x + ball.direction.x, ball.y + ball.direction.y)
139 | if (Math.abs(ball.y) > this.maxY) {
140 | SideAudio.play()
141 | ball.direction.y *= -1
142 | }
143 | }
144 |
145 | updatePlayerState (P) {
146 | switch (P.direction) {
147 | case Movement.Up:
148 | P.y = Math.min(P.y + P.speed, this.maxY - P.height / 2)
149 | break
150 | case Movement.Down:
151 | P.y = Math.max(P.y - P.speed, -this.maxY + P.height / 2)
152 | break
153 | }
154 | }
155 |
156 | syncState () {
157 | this.renderer.setOldPositions(this.state.ball.oldPositions)
158 | this.renderer.setGameStage(this.stage)
159 | this.renderer.setLeftPaddlePosition(this.state.P1.x, this.state.P1.y)
160 | this.renderer.setRightPaddlePosition(this.state.P2.x, this.state.P2.y)
161 | this.renderer.setBallPosition(this.state.ball.x, this.state.ball.y)
162 | this.renderer.setScore(this.state.P1.score, this.state.P2.score)
163 | }
164 |
165 | draw () {
166 | this.renderer.draw()
167 | }
168 |
169 | addEventListeners () {
170 | this.canvas.addEventListener('mousedown', (e) => {
171 | const hitboxSize = 50
172 | const click = getCursorPosition(this.canvas, e)
173 |
174 | if (Math.abs(this.center.x - click.x) < hitboxSize &&
175 | Math.abs(this.center.y - click.y) < hitboxSize &&
176 | this.stage !== GameStage.Playing) {
177 | this.state = new State()
178 | this.stage = GameStage.Playing
179 | }
180 | })
181 |
182 | document.addEventListener('keydown', (k) => {
183 | switch (k.code) {
184 | case 'ArrowUp':
185 | this.state.P2.direction = Movement.Up
186 | break
187 | case 'ArrowDown':
188 | this.state.P2.direction = Movement.Down
189 | break
190 | case 'KeyW':
191 | this.state.P1.direction = Movement.Up
192 | break
193 | case 'KeyS':
194 | this.state.P1.direction = Movement.Down
195 | break
196 | }
197 | })
198 |
199 | document.addEventListener('keyup', (k) => {
200 | switch (k.code) {
201 | case 'ArrowUp':
202 | if (this.state.P2.direction === Movement.Up) {
203 | this.state.P2.direction = Movement.None
204 | }
205 | break
206 | case 'ArrowDown':
207 | if (this.state.P2.direction === Movement.Down) {
208 | this.state.P2.direction = Movement.None
209 | }
210 | break
211 | case 'KeyW':
212 | if (this.state.P1.direction === Movement.Up) {
213 | this.state.P1.direction = Movement.None
214 | }
215 | break
216 | case 'KeyS':
217 | if (this.state.P1.direction === Movement.Down) {
218 | this.state.P1.direction = Movement.None
219 | }
220 | break
221 | }
222 | })
223 | }
224 | }
225 |
226 | // https://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element
227 | function getCursorPosition (canvas, event) {
228 | const rect = canvas.getBoundingClientRect()
229 | const x = event.clientX - rect.left
230 | const y = event.clientY - rect.top
231 | return { x: x, y: y }
232 | }
233 |
234 | class State {
235 | P1
236 | P2
237 | ball
238 | terminating
239 |
240 | constructor () {
241 | this.ball = new Ball()
242 | this.P1 = new Player(-0.9)
243 | this.P2 = new Player(0.9)
244 | this.terminating = false
245 | }
246 |
247 | gameOver () {
248 | return this.P1.score > 9 || this.P2.score > 9
249 | }
250 |
251 | currentStage () {
252 | if (this.P1.score >= 9) {
253 | return GameStage.P1Win
254 | } else if (this.P2.score >= 9) {
255 | return GameStage.P2Win
256 | }
257 | return GameStage.Playing
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/pong/shaders/shader.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | varying mediump vec2 screenPosition;
4 |
5 | //
6 | // GAME STATE
7 | //
8 | uniform vec2 score;
9 | uniform vec2 ballPosition;
10 | uniform vec2 leftPaddlePosition;
11 | uniform vec2 rightPaddlePosition;
12 | uniform float gameStage;
13 | uniform vec2 oldPositions[30];
14 | uniform float time;
15 |
16 | //
17 | // CURVE
18 | //
19 | vec2 curveAmount = vec2(0.8, 0.6);
20 | float caseBorder = 0.0125;
21 |
22 |
23 | float ballRadius = 0.02;
24 | vec2 paddleSize = vec2(0.04, 0.24);
25 |
26 |
27 | //
28 | // DRAWING FUNCTIONS
29 | //
30 | bool isInRect(vec2 position, vec2 center, vec2 size)
31 | {
32 | vec2 delta = abs(center - position); // delta for paddle 0
33 | return delta.x < (size.x/2.0) && delta.y < (size.y/2.0);
34 | }
35 |
36 | float pixelSize = 0.03;
37 |
38 | // xxx
39 | // x x
40 | // x x
41 | // x x
42 | // xxx
43 | bool is0(vec2 position, vec2 pos)
44 | {
45 | return
46 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
47 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||
48 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
49 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));
50 | }
51 |
52 |
53 | // x
54 | // x
55 | // x
56 | // x
57 | // x
58 | bool is1(vec2 position, vec2 pos)
59 | {
60 | return isInRect(position, pos, vec2(pixelSize, pixelSize * 5.0));
61 | }
62 |
63 | // xxx
64 | // x
65 | // xxx
66 | // x
67 | // xxx
68 | bool is2(vec2 position, vec2 pos)
69 | {
70 | return
71 | isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||
72 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
73 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
74 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||
75 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));
76 | }
77 |
78 | // xxx
79 | // x
80 | // xxx
81 | // x
82 | // xxx
83 | bool is3(vec2 position, vec2 pos)
84 | {
85 | return
86 | isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||
87 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
88 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
89 | isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 3.0 * pixelSize));
90 | }
91 |
92 | // x x
93 | // x x
94 | // xxx
95 | // x
96 | // x
97 | bool is4(vec2 position, vec2 pos)
98 | {
99 | return
100 | isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||
101 | isInRect(position, vec2(pos.x - pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||
102 | isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize));
103 | }
104 |
105 | // xxx
106 | // x
107 | // xxx
108 | // x
109 | // xxx
110 | bool is5(vec2 position, vec2 pos)
111 | {
112 | return
113 | isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||
114 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
115 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
116 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||
117 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));
118 | }
119 |
120 | // xxx
121 | // x
122 | // xxx
123 | // x x
124 | // xxx
125 | bool is6(vec2 position, vec2 pos)
126 | {
127 | return
128 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
129 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||
130 | isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||
131 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
132 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0));
133 | }
134 |
135 | // xxx
136 | // x
137 | // x
138 | // x
139 | // x
140 | bool is7(vec2 position, vec2 pos)
141 | {
142 | return
143 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
144 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));
145 | }
146 |
147 | // xxx
148 | // x x
149 | // xxx
150 | // x x
151 | // xxx
152 | bool is8(vec2 position, vec2 pos)
153 | {
154 | return
155 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
156 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||
157 | isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||
158 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
159 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0));
160 | }
161 |
162 | // xxx
163 | // x x
164 | // xxx
165 | // x
166 | // xxx
167 | bool is9(vec2 position, vec2 pos)
168 | {
169 | return
170 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
171 | isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||
172 | isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||
173 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
174 | isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));
175 | }
176 |
177 | // xxx
178 | // x x
179 | // xxx
180 | // x
181 | // x
182 | bool isP(vec2 position, vec2 pos)
183 | {
184 | return
185 | isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||
186 | isInRect(position, pos, vec2(pixelSize, pixelSize)) ||
187 | isInRect(position, vec2(pos.x + pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||
188 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize, pixelSize));
189 | }
190 |
191 | bool isO(vec2 position, vec2 pos)
192 | {
193 | return is0(position, pos);
194 | }
195 |
196 | // xxx
197 | // x x
198 | // x x
199 | // x x
200 | // x x
201 | bool isN(vec2 position, vec2 pos)
202 | {
203 | return
204 | isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||
205 | isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||
206 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(3.0 * pixelSize, pixelSize));
207 | }
208 |
209 |
210 | // xxx
211 | // x
212 | // x|x
213 | // x x
214 | // xxx
215 | bool isG(vec2 position, vec2 pos)
216 | {
217 | return
218 | isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
219 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||
220 | isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||
221 | isInRect(position, vec2(pos.x + pixelSize, pos.y - pixelSize), vec2(pixelSize)) ||
222 | isInRect(position, vec2(pos.x + 0.75 * pixelSize, pos.y), vec2(1.5 * pixelSize, pixelSize));
223 | }
224 |
225 | // x
226 | // x
227 | // x
228 | //
229 | // x
230 | bool isExclamationMark(vec2 position, vec2 pos)
231 | {
232 | return
233 | isInRect(position, vec2(pos.x, pos.y + pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||
234 | isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize));
235 | }
236 |
237 |
238 | bool isPONG(vec2 position, vec2 pos)
239 | {
240 | return
241 | isP(position, vec2(pos.x - 6.0 * pixelSize, pos.y)) ||
242 | isO(position, vec2(pos.x - 2.0 * pixelSize, pos.y)) ||
243 | isN(position, vec2(pos.x + 2.0 * pixelSize, pos.y)) ||
244 | isG(position, vec2(pos.x + 6.0 * pixelSize, pos.y));
245 | }
246 |
247 |
248 | bool isTriangle(vec2 position, vec2 size, vec2 pos)
249 | {
250 | float top = pos.y + size.y;
251 | float bottom = pos.y - size.y;
252 | float start = pos.x;
253 | float end = pos.x + size.x;
254 |
255 | float slope = size.y/size.x;
256 | float relativeX = position.x - start;
257 |
258 | return
259 | position.x > start && position.x < end && // x within triangle
260 | (position.y < top - slope * relativeX && position.y > bottom + slope * relativeX); // y within triangle
261 |
262 | }
263 |
264 | bool isNumber(float number, vec2 position, vec2 numberPosition)
265 | { // GLSL is a beautiful language
266 | if (number == 0.0) { return is0(position, numberPosition); }
267 | if (number == 1.0) { return is1(position, numberPosition); }
268 | if (number == 2.0) { return is2(position, numberPosition); }
269 | if (number == 3.0) { return is3(position, numberPosition); }
270 | if (number == 4.0) { return is4(position, numberPosition); }
271 | if (number == 5.0) { return is5(position, numberPosition); }
272 | if (number == 6.0) { return is6(position, numberPosition); }
273 | if (number == 7.0) { return is7(position, numberPosition); }
274 | if (number == 8.0) { return is8(position, numberPosition); }
275 | if (number == 9.0) { return is9(position, numberPosition); }
276 | return false;
277 | }
278 |
279 | bool isP1score(vec2 position) {
280 | return isNumber(score.x, position, vec2(-4.0 * pixelSize, 18.0 * pixelSize));
281 | }
282 |
283 | bool isP2score(vec2 position) {
284 | return isNumber(score.y, position, vec2(4.0 * pixelSize, 18.0 * pixelSize));
285 | }
286 |
287 | bool isLine(vec2 position) {
288 | return
289 | abs(position.x) < pixelSize / 2.0 &&
290 | mod(position.y - pixelSize * 1.5, pixelSize * 2.0) <= pixelSize;
291 | }
292 |
293 | bool isWinLine(vec2 position) {
294 | return isLine(position) &&
295 | (position.y > pixelSize * 3.0 || position.y < pixelSize * -3.0);
296 | }
297 |
298 | bool isPlayButton(vec2 position) {
299 | return isTriangle(position, vec2(0.16, 0.1), vec2(-0.06, 0));
300 | }
301 |
302 | //
303 | // GAMESTAGE SPECIFIC RENDERING
304 | //
305 | vec4 welcome(vec2 position) { // GameStage.Welcome
306 | if (isPONG(position, vec2(0, 0.3)) ||
307 | isPlayButton(position))
308 | {
309 | return vec4(1, 1, 1, 1);
310 | }
311 | return vec4(0, 0, 0, 1.0);
312 | }
313 |
314 | vec4 playing(vec2 position) // GameStage.Playing
315 | {
316 | bool isLeftPaddle = isInRect(position, leftPaddlePosition, paddleSize);
317 | bool isRightPaddle = isInRect(position, rightPaddlePosition, paddleSize);
318 | bool isBall = isInRect(position, ballPosition, vec2(ballRadius * 2.0, ballRadius * 2.0));
319 |
320 | if (isLeftPaddle ||
321 | isRightPaddle ||
322 | isBall ||
323 | isLine(position) ||
324 | isP1score(position) ||
325 | isP2score(position))
326 | {
327 | return vec4(1, 1, 1, 1);
328 | }
329 |
330 | // Render tail
331 | for (int i = 29; i >= 0; i--) {
332 | vec2 oldPos = oldPositions[i];
333 | float scale = float(i) / 30.0;
334 | if (isInRect(position, oldPos, vec2(ballRadius * 2.0 * scale))) {
335 | return vec4(scale, scale, scale, 1.0);
336 | }
337 | }
338 |
339 | return vec4(0, 0, 0, 1.0);
340 | }
341 |
342 | vec4 p1win(vec2 position) // GameStage.P1Win
343 | {
344 | if (isP(position, vec2(-19.0 * pixelSize, 0)) ||
345 | is1(position, vec2(-16.0 * pixelSize, 0)) ||
346 | isExclamationMark(position, vec2(-14.0 * pixelSize, 0)) ||
347 | isP1score(position) ||
348 | isP2score(position) ||
349 | isWinLine(position) ||
350 | isPlayButton(position)
351 | )
352 | {
353 | return vec4(1, 1, 1, 1);
354 | }
355 | return vec4(0, 0, 0, 1.0);
356 | }
357 |
358 | vec4 p2win(vec2 position) // GameStage.P2Win
359 | {
360 | if (isP(position, vec2(14.0 * pixelSize, 0)) ||
361 | is2(position, vec2(18.0 * pixelSize, 0)) ||
362 | isExclamationMark(position, vec2(21.0 * pixelSize, 0)) ||
363 | isP1score(position) ||
364 | isP2score(position) ||
365 | isWinLine(position) ||
366 | isPlayButton(position)
367 | )
368 | {
369 | return vec4(1, 1, 1, 1);
370 | }
371 | return vec4(0, 0, 0, 1.0);
372 | }
373 |
374 | vec4 scanline(vec4 color, vec2 position) {
375 | float lineHeight = 0.0104;
376 | float darkness = 0.9;
377 | float speed = 0.0104;
378 |
379 | float y = mod(position.y + (time / 60.0) * speed, lineHeight);
380 | if (y < lineHeight/2.0) {
381 | return color;
382 | } else {
383 | return vec4(color.x, color.y, color.z, darkness);
384 | }
385 | }
386 |
387 | vec4 colorAt(vec2 position) {
388 | // Map position from [0, 1] to [-1, 1] to get world space color
389 | position *= 2.0;
390 | position -= vec2(1.0);
391 |
392 | vec4 color;
393 |
394 | if (abs(position.y) > 0.666) { // out of render area
395 | return vec4(0, 0, 0, 1);
396 | }
397 |
398 | if (gameStage == 0.0) { // GameStage.Welcome
399 | color = welcome(position);
400 | } else if (gameStage == 1.0) { // GameStage.Playing
401 | color = playing(position);
402 | } else if (gameStage == 2.0) { // GameStage.P1Win
403 | color = p1win(position);
404 | } else if (gameStage == 3.0) { // GameStage.P2Win
405 | color = p2win(position);
406 | }
407 |
408 | // Apply scanline effect
409 | color = scanline(color, position);
410 |
411 | return color;
412 | }
413 |
414 |
415 | vec4 curve(vec2 position) {
416 | // Map position from [-1, 1] to [0, 1] for curve effect
417 | position += vec2(1.0);
418 | position /= 2.0;
419 |
420 | // Using this https://github.com/wessles/GLSL-CRT/blob/master/shader.frag algorithm
421 | float dx = abs(0.5 - position.x);
422 | float dy = abs(0.5 - position.y);
423 | dx *= dx;
424 | dy *= dy;
425 |
426 | position.x -= 0.5;
427 | position.x *= 1.0 + (dy * curveAmount.x);
428 | position.x += 0.5;
429 |
430 | position.y -= 0.5;
431 | position.y *= 1.0 + (dx * curveAmount.y);
432 | position.y += 0.5;
433 |
434 | // Draw color from world space
435 | vec4 color = colorAt(position);
436 | color += sin(position.y) * 0.02;
437 |
438 | if(position.y > 1.0 || position.x < 0.0 || position.x > 1.0 || position.y < 0.0)
439 | color = vec4(0, 0, 0, 1);
440 |
441 | return color;
442 | }
443 |
444 | void main() {
445 | gl_FragColor = curve(screenPosition);
446 | }
447 |
--------------------------------------------------------------------------------
/docs/js/app.4b46cf16.js:
--------------------------------------------------------------------------------
1 | (function(e){function i(i){for(var t,r,a=i[0],c=i[1],l=i[2],p=0,u=[];p30&&this.oldPositions.shift(),this.x=e,this.y=i}}]),Object(d["a"])(e,[{key:"left",value:function(){return this.x-this.radius}},{key:"right",value:function(){return this.x+this.radius}},{key:"top",value:function(){return this.y+this.radius}},{key:"bottom",value:function(){return this.y-this.radius}}]),e}()),m=new Audio(n("805d")),z=new Audio(n("c21e")),P=new Audio(n("e57d")),w=new Audio(n("dc58")),R={Welcome:0,Playing:1,P1Win:2,P2Win:3},I=function(){function e(i,n,t){Object(h["a"])(this,e),Object(f["a"])(this,"countdown",void 0),Object(f["a"])(this,"stage",void 0),Object(f["a"])(this,"canvas",void 0),Object(f["a"])(this,"center",void 0),Object(f["a"])(this,"renderer",void 0),Object(f["a"])(this,"state",void 0),Object(f["a"])(this,"maxY",void 0),Object(f["a"])(this,"time",void 0),this.countdown=60,this.time=0,this.stage="undefined"!==typeof t?t:R.Playing,this.canvas=n,this.renderer=i,this.maxY=.65,this.state=new k,this.center={x:n.width/2,y:n.height/2}}return Object(d["a"])(e,[{key:"tick",value:function(){this.time++,this.renderer.setTime(this.time),this.stage===R.Playing?(this.state=this.transition(this.state),this.syncState()):this.renderer.setGameStage(this.stage),this.draw()}},{key:"transition",value:function(e){if(this.countdown--,this.stage=this.state.currentStage(),this.updatePlayerState(e.P1),this.updatePlayerState(e.P2),this.countdown>0)return e;if(0===this.countdown&&P.play(),this.updateBallState(e.ball),e.ball.x<=-1){var i=new k;return i.P1=e.P1,i.P2=e.P2,i.P2.score++,m.play(),this.countdown=60,i}if(e.ball.x>=1){var n=new k;return n.P1=e.P1,n.P2=e.P2,n.P1.score++,this.countdown=60,m.play(),n}if(e.terminating)return e;var t=5*Math.PI/12;if(e.ball.left()e.P1.bottom()||e.ball.top()e.P1.bottom()){z.play();var o=e.P1.y-e.ball.y,s=o/(e.P1.height/2),r=s*t;e.ball.speed*=e.ball.acceleration,e.ball.direction.x=e.ball.speed*Math.cos(r),e.ball.direction.y=-e.ball.speed*Math.sin(r)}else e.terminating=!0;if(e.ball.right()>e.P2.left())if(e.ball.bottom()e.P2.bottom()||e.ball.top()e.P2.bottom()){z.play();var a=e.P2.y-e.ball.y,c=a/(e.P2.height/2),l=c*t;e.ball.speed*=e.ball.acceleration,e.ball.direction.x=e.ball.speed*-Math.cos(l),e.ball.direction.y=e.ball.speed*-Math.sin(l)}else e.terminating=!0;return this.state}},{key:"updateBallState",value:function(e){e.setLocation(e.x+e.direction.x,e.y+e.direction.y),Math.abs(e.y)>this.maxY&&(w.play(),e.direction.y*=-1)}},{key:"updatePlayerState",value:function(e){switch(e.direction){case b.Up:e.y=Math.min(e.y+e.speed,this.maxY-e.height/2);break;case b.Down:e.y=Math.max(e.y-e.speed,-this.maxY+e.height/2);break}}},{key:"syncState",value:function(){this.renderer.setOldPositions(this.state.ball.oldPositions),this.renderer.setGameStage(this.stage),this.renderer.setLeftPaddlePosition(this.state.P1.x,this.state.P1.y),this.renderer.setRightPaddlePosition(this.state.P2.x,this.state.P2.y),this.renderer.setBallPosition(this.state.ball.x,this.state.ball.y),this.renderer.setScore(this.state.P1.score,this.state.P2.score)}},{key:"draw",value:function(){this.renderer.draw()}},{key:"addEventListeners",value:function(){var e=this;this.canvas.addEventListener("mousedown",(function(i){var n=50,t=O(e.canvas,i);Math.abs(e.center.x-t.x)9||this.P2.score>9}},{key:"currentStage",value:function(){return this.P1.score>=9?R.P1Win:this.P2.score>=9?R.P2Win:R.Playing}}]),e}(),j=(n("ddb0"),n("3835")),A=n("b85c"),L=n("20e7"),U=(n("ac1f"),n("5319"),function(){function e(i,n,t){Object(h["a"])(this,e),Object(f["a"])(this,"gl",void 0),Object(f["a"])(this,"program",void 0),this.gl=i,this.program=this.initShaderProgram(i,n,t)}return Object(d["a"])(e,[{key:"initShaderProgram",value:function(e,i,n){var t=this.loadShader(e,e.VERTEX_SHADER,i),o=this.loadShader(e,e.FRAGMENT_SHADER,n),s=e.createProgram();return e.attachShader(s,t),e.attachShader(s,o),e.linkProgram(s),e.getProgramParameter(s,e.LINK_STATUS)?s:(alert("Unable to initialize the shader program: ".concat(e.getProgramInfoLog(s))),null)}},{key:"preprocessSource",value:function(e,i){var n=/42\/\/\$(.*)\$\/\//g;return e.replace(n,(function(e,n){return i[n]}))}},{key:"loadShader",value:function(e,i,n){var t=e.createShader(i);return e.shaderSource(t,n),e.compileShader(t),e.getShaderParameter(t,e.COMPILE_STATUS)?t:(console.error("An error occurred compiling the shaders:\n".concat(e.getShaderInfoLog(t))),e.deleteShader(t),null)}},{key:"use",value:function(){this.gl.useProgram(this.program)}},{key:"setUniform1f",value:function(e,i){this.gl.uniform1f(this.getUniformLocation(e),i)}},{key:"setUniformMatrix4fv",value:function(e,i){this.gl.uniformMatrix4fv(this.getUniformLocation(e),!1,i)}},{key:"setUniform2fv",value:function(e,i){this.gl.uniform2fv(this.getUniformLocation(e),i)}},{key:"setUniform3fv",value:function(e,i){this.gl.uniform3fv(this.getUniformLocation(e),i)}},{key:"getUniformLocation",value:function(e){return this.gl.getUniformLocation(this.program,e)}},{key:"getAttribLocation",value:function(e){return this.gl.getAttribLocation(this.program,e)}},{key:"delete",value:function(){this.gl.deleteProgram(this.program)}}]),e}()),E="precision highp float;\n\nvarying mediump vec2 screenPosition;\n\n//\n// GAME STATE\n//\nuniform vec2 score;\nuniform vec2 ballPosition;\nuniform vec2 leftPaddlePosition;\nuniform vec2 rightPaddlePosition;\nuniform float gameStage;\nuniform vec2 oldPositions[30];\nuniform float time;\n\n//\n// CURVE\n//\nvec2 curveAmount = vec2(0.8, 0.6);\nfloat caseBorder = 0.0125;\n\n\nfloat ballRadius = 0.02;\nvec2 paddleSize = vec2(0.04, 0.24);\n\n\n//\n// DRAWING FUNCTIONS\n//\nbool isInRect(vec2 position, vec2 center, vec2 size)\n{\n vec2 delta = abs(center - position); // delta for paddle 0\n return delta.x < (size.x/2.0) && delta.y < (size.y/2.0);\n}\n\nfloat pixelSize = 0.03;\n\n// xxx\n// x x\n// x x\n// x x\n// xxx\nbool is0(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\n}\n\n\n// x\n// x\n// x\n// x\n// x\nbool is1(vec2 position, vec2 pos)\n{\n return isInRect(position, pos, vec2(pixelSize, pixelSize * 5.0));\n}\n\n// xxx\n// x\n// xxx\n// x\n// xxx\nbool is2(vec2 position, vec2 pos)\n{\n return\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));\n}\n\n// xxx\n// x\n// xxx\n// x\n// xxx\nbool is3(vec2 position, vec2 pos)\n{\n return\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 3.0 * pixelSize));\n}\n\n// x x\n// x x\n// xxx\n// x\n// x\nbool is4(vec2 position, vec2 pos)\n{\n return\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize));\n}\n\n// xxx\n// x\n// xxx\n// x\n// xxx\nbool is5(vec2 position, vec2 pos)\n{\n return\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));\n}\n\n// xxx\n// x\n// xxx\n// x x\n// xxx\nbool is6(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0));\n}\n\n// xxx\n// x\n// x\n// x\n// x\nbool is7(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\n}\n\n// xxx\n// x x\n// xxx\n// x x\n// xxx\nbool is8(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0));\n}\n\n// xxx\n// x x\n// xxx\n// x\n// xxx\nbool is9(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\n}\n\n// xxx\n// x x\n// xxx\n// x\n// x\nbool isP(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\n isInRect(position, pos, vec2(pixelSize, pixelSize)) ||\n isInRect(position, vec2(pos.x + pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize, pixelSize));\n}\n\nbool isO(vec2 position, vec2 pos)\n{\n return is0(position, pos);\n}\n\n// xxx\n// x x\n// x x\n// x x\n// x x\nbool isN(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(3.0 * pixelSize, pixelSize));\n}\n\n\n// xxx\n// x\n// x|x\n// x x\n// xxx\nbool isG(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||\n isInRect(position, vec2(pos.x + pixelSize, pos.y - pixelSize), vec2(pixelSize)) ||\n isInRect(position, vec2(pos.x + 0.75 * pixelSize, pos.y), vec2(1.5 * pixelSize, pixelSize));\n}\n\n// x\n// x\n// x\n//\n// x\nbool isExclamationMark(vec2 position, vec2 pos)\n{\n return\n isInRect(position, vec2(pos.x, pos.y + pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize));\n}\n\n\nbool isPONG(vec2 position, vec2 pos)\n{\n return\n isP(position, vec2(pos.x - 6.0 * pixelSize, pos.y)) ||\n isO(position, vec2(pos.x - 2.0 * pixelSize, pos.y)) ||\n isN(position, vec2(pos.x + 2.0 * pixelSize, pos.y)) ||\n isG(position, vec2(pos.x + 6.0 * pixelSize, pos.y));\n}\n\n\nbool isTriangle(vec2 position, vec2 size, vec2 pos)\n{\n float top = pos.y + size.y;\n float bottom = pos.y - size.y;\n float start = pos.x;\n float end = pos.x + size.x;\n\n float slope = size.y/size.x;\n float relativeX = position.x - start;\n\n return\n position.x > start && position.x < end && // x within triangle\n (position.y < top - slope * relativeX && position.y > bottom + slope * relativeX); // y within triangle\n\n}\n\nbool isNumber(float number, vec2 position, vec2 numberPosition)\n{ // GLSL is a beautiful language\n if (number == 0.0) { return is0(position, numberPosition); }\n if (number == 1.0) { return is1(position, numberPosition); }\n if (number == 2.0) { return is2(position, numberPosition); }\n if (number == 3.0) { return is3(position, numberPosition); }\n if (number == 4.0) { return is4(position, numberPosition); }\n if (number == 5.0) { return is5(position, numberPosition); }\n if (number == 6.0) { return is6(position, numberPosition); }\n if (number == 7.0) { return is7(position, numberPosition); }\n if (number == 8.0) { return is8(position, numberPosition); }\n if (number == 9.0) { return is9(position, numberPosition); }\n return false;\n}\n\nbool isP1score(vec2 position) {\n return isNumber(score.x, position, vec2(-4.0 * pixelSize, 18.0 * pixelSize));\n}\n\nbool isP2score(vec2 position) {\n return isNumber(score.y, position, vec2(4.0 * pixelSize, 18.0 * pixelSize));\n}\n\nbool isLine(vec2 position) {\n return\n abs(position.x) < pixelSize / 2.0 &&\n mod(position.y - pixelSize * 1.5, pixelSize * 2.0) <= pixelSize;\n}\n\nbool isWinLine(vec2 position) {\n return isLine(position) &&\n (position.y > pixelSize * 3.0 || position.y < pixelSize * -3.0);\n}\n\nbool isPlayButton(vec2 position) {\n return isTriangle(position, vec2(0.16, 0.1), vec2(-0.06, 0));\n}\n\n//\n// GAMESTAGE SPECIFIC RENDERING\n//\nvec4 welcome(vec2 position) { // GameStage.Welcome\n if (isPONG(position, vec2(0, 0.3)) ||\n isPlayButton(position))\n {\n return vec4(1, 1, 1, 1);\n }\n return vec4(0, 0, 0, 1.0);\n}\n\nvec4 playing(vec2 position) // GameStage.Playing\n{\n bool isLeftPaddle = isInRect(position, leftPaddlePosition, paddleSize);\n bool isRightPaddle = isInRect(position, rightPaddlePosition, paddleSize);\n bool isBall = isInRect(position, ballPosition, vec2(ballRadius * 2.0, ballRadius * 2.0));\n\n if (isLeftPaddle ||\n isRightPaddle ||\n isBall ||\n isLine(position) ||\n isP1score(position) ||\n isP2score(position))\n {\n return vec4(1, 1, 1, 1);\n }\n\n // Render tail\n for (int i = 29; i >= 0; i--) {\n vec2 oldPos = oldPositions[i];\n float scale = float(i) / 30.0;\n if (isInRect(position, oldPos, vec2(ballRadius * 2.0 * scale))) {\n return vec4(scale, scale, scale, 1.0);\n }\n }\n\n return vec4(0, 0, 0, 1.0);\n}\n\nvec4 p1win(vec2 position) // GameStage.P1Win\n{\n if (isP(position, vec2(-19.0 * pixelSize, 0)) ||\n is1(position, vec2(-16.0 * pixelSize, 0)) ||\n isExclamationMark(position, vec2(-14.0 * pixelSize, 0)) ||\n isP1score(position) ||\n isP2score(position) ||\n isWinLine(position) ||\n isPlayButton(position)\n )\n {\n return vec4(1, 1, 1, 1);\n }\n return vec4(0, 0, 0, 1.0);\n}\n\nvec4 p2win(vec2 position) // GameStage.P2Win\n{\n if (isP(position, vec2(14.0 * pixelSize, 0)) ||\n is2(position, vec2(18.0 * pixelSize, 0)) ||\n isExclamationMark(position, vec2(21.0 * pixelSize, 0)) ||\n isP1score(position) ||\n isP2score(position) ||\n isWinLine(position) ||\n isPlayButton(position)\n )\n {\n return vec4(1, 1, 1, 1);\n }\n return vec4(0, 0, 0, 1.0);\n}\n\nvec4 scanline(vec4 color, vec2 position) {\n float lineHeight = 0.0104;\n float darkness = 0.9;\n float speed = 0.0104;\n\n float y = mod(position.y + (time / 60.0) * speed, lineHeight);\n if (y < lineHeight/2.0) {\n return color;\n } else {\n return vec4(color.x, color.y, color.z, darkness);\n }\n}\n\nvec4 colorAt(vec2 position) {\n // Map position from [0, 1] to [-1, 1] to get world space color\n position *= 2.0;\n position -= vec2(1.0);\n\n vec4 color;\n\n if (abs(position.y) > 0.666) { // out of render area\n return vec4(0, 0, 0, 1);\n }\n\n if (gameStage == 0.0) { // GameStage.Welcome\n color = welcome(position);\n } else if (gameStage == 1.0) { // GameStage.Playing\n color = playing(position);\n } else if (gameStage == 2.0) { // GameStage.P1Win\n color = p1win(position);\n } else if (gameStage == 3.0) { // GameStage.P2Win\n color = p2win(position);\n }\n\n // Apply scanline effect\n color = scanline(color, position);\n\n return color;\n}\n\n\nvec4 curve(vec2 position) {\n // Map position from [-1, 1] to [0, 1] for curve effect\n position += vec2(1.0);\n position /= 2.0;\n\n // Using this https://github.com/wessles/GLSL-CRT/blob/master/shader.frag algorithm\n float dx = abs(0.5 - position.x);\n float dy = abs(0.5 - position.y);\n dx *= dx;\n dy *= dy;\n\n position.x -= 0.5;\n position.x *= 1.0 + (dy * curveAmount.x);\n position.x += 0.5;\n\n position.y -= 0.5;\n position.y *= 1.0 + (dx * curveAmount.y);\n position.y += 0.5;\n\n // Draw color from world space\n vec4 color = colorAt(position);\n color += sin(position.y) * 0.02;\n\n if(position.y > 1.0 || position.x < 0.0 || position.x > 1.0 || position.y < 0.0)\n color = vec4(0, 0, 0, 1);\n\n return color;\n}\n\nvoid main() {\n gl_FragColor = curve(screenPosition);\n}\n",M="precision highp float;\n\nattribute vec4 aVertexPosition;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nvarying mediump vec2 screenPosition;\n\nvoid main() {\n screenPosition = aVertexPosition.xy;\n gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;\n}\n",T=function(){function e(i){Object(h["a"])(this,e),Object(f["a"])(this,"gl",void 0),Object(f["a"])(this,"shader",void 0),Object(f["a"])(this,"buffers",void 0),this.gl=i,this.shader=new U(i,M,E),this.buffers=this.initBuffers(),this.shader.use(),this.setDefaultPositions()}return Object(d["a"])(e,[{key:"recompileShader",value:function(e){this.shader.delete(),this.shader=new U(this.gl,M,E,e)}},{key:"initBuffers",value:function(){var e=this.gl.createBuffer();this.gl.bindBuffer(this.gl.ARRAY_BUFFER,e);var i=[-1,1,1,1,-1,-1,1,-1];return this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array(i),this.gl.STATIC_DRAW),{position:e}}},{key:"draw",value:function(){this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.clearDepth(1),this.gl.enable(this.gl.DEPTH_TEST),this.gl.depthFunc(this.gl.LEQUAL),this.gl.clear(this.gl.COLOR_BUFFER_BIT|this.gl.DEPTH_BUFFER_BIT);var e=29.8*Math.PI/180,i=this.gl.canvas.clientWidth/this.gl.canvas.clientHeight,n=.1,t=100,o=L["a"].create();L["a"].perspective(o,e,i,n,t);var s=L["a"].create();L["a"].translate(s,s,[0,0,-2.5]);var r=this.shader.getAttribLocation("aVertexPosition");this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.buffers.position),this.gl.vertexAttribPointer(r,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(r),this.shader.use(),this.shader.setUniformMatrix4fv("uProjectionMatrix",o),this.shader.setUniformMatrix4fv("uModelViewMatrix",s);var a=0,c=4;this.gl.drawArrays(this.gl.TRIANGLE_STRIP,a,c)}},{key:"setOldPositions",value:function(e){var i,n=Object(A["a"])(e.entries());try{for(n.s();!(i=n.n()).done;){var t=Object(j["a"])(i.value,2),o=t[0],s=t[1];this.shader.setUniform2fv("oldPositions[".concat(o,"]"),s)}}catch(r){n.e(r)}finally{n.f()}}},{key:"setTime",value:function(e){this.shader.setUniform1f("time",e)}},{key:"setGameStage",value:function(e){this.shader.setUniform1f("gameStage",e)}},{key:"setDefaultPositions",value:function(){this.setLeftPaddlePosition(-.9,0),this.setRightPaddlePosition(.9,0),this.setBallPosition(0,0)}},{key:"setLeftPaddlePosition",value:function(e,i){this.shader.setUniform2fv("leftPaddlePosition",new Float32Array([e,i]))}},{key:"setRightPaddlePosition",value:function(e,i){this.shader.setUniform2fv("rightPaddlePosition",new Float32Array([e,i]))}},{key:"setBallPosition",value:function(e,i){this.shader.setUniform2fv("ballPosition",new Float32Array([e,i]))}},{key:"setScore",value:function(e,i){this.shader.setUniform2fv("score",new Float32Array([e,i]))}}]),e}(),_={name:"Home",data:function(){return{pong:null,pongRenderer:null}},mounted:function(){var e=this,i=document.querySelector("#glCanvas"),n=i.getContext("webgl");null===n&&alert("Unable to initialize WebGL. Your browser or machine may not support it."),this.pongRenderer=new T(n),this.pong=new I(this.pongRenderer,i,R.Welcome),this.pong.addEventListeners();var t=function i(n){e.pong.tick(),requestAnimationFrame(i)};requestAnimationFrame(t)}},F=_,B=(n("21bb"),Object(r["a"])(F,u,v,!1,null,null,null)),G=B.exports;t["a"].use(x["a"]);var N=[{path:"/",name:"Home",component:G},{path:"/about",name:"About",component:function(){return n.e("about").then(n.bind(null,"f820"))}}],D=new x["a"]({mode:"history",base:"/infogr-pong/",routes:N}),W=D,C=n("2f62");t["a"].use(C["a"]);var H=new C["a"].Store({state:{},mutations:{},actions:{},modules:{}});t["a"].config.productionTip=!1,new t["a"]({router:W,store:H,render:function(e){return e(l)}}).$mount("#app")},"5c0b":function(e,i,n){"use strict";var t=n("9c0c"),o=n.n(t);o.a},"805d":function(e,i,n){e.exports=n.p+"media/score.63ce9ecd.wav"},"9c0c":function(e,i,n){},c21e:function(e,i,n){e.exports=n.p+"media/hit.f8648124.wav"},dc58:function(e,i,n){e.exports=n.p+"media/side.f37fcc5f.wav"},e57d:function(e,i,n){e.exports=n.p+"media/start.1f4848ab.wav"}});
2 | //# sourceMappingURL=app.4b46cf16.js.map
--------------------------------------------------------------------------------
/docs/js/app.4b46cf16.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/views/Home.vue?da68","webpack:///./src/App.vue?bf48","webpack:///./src/App.vue","webpack:///./src/registerServiceWorker.js","webpack:///./src/views/Home.vue?669d","webpack:///./src/pong/movement.js","webpack:///./src/pong/player.js","webpack:///./src/pong/ball.js","webpack:///./src/pong/pong.js","webpack:///./src/pong/shader.js","webpack:///./src/pong/shaders/shader.frag","webpack:///./src/pong/shaders/shader.vert","webpack:///./src/pong/pong-renderer.js","webpack:///src/views/Home.vue","webpack:///./src/views/Home.vue?493c","webpack:///./src/views/Home.vue","webpack:///./src/router/index.js","webpack:///./src/store/index.js","webpack:///./src/main.js","webpack:///./src/App.vue?c650","webpack:///./src/assets/score.wav","webpack:///./src/assets/hit.wav","webpack:///./src/assets/side.wav","webpack:///./src/assets/start.wav"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","jsonpScriptSrc","p","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","window","oldJsonpFunction","slice","_vm","this","_h","$createElement","_c","_self","attrs","staticRenderFns","component","register","process","ready","log","registered","cached","updatefound","updated","offline","_m","staticClass","Movement","None","Up","Down","Player","x","width","height","y","speed","direction","score","Ball","radius","startDirections","Math","floor","random","acceleration","oldPositions","Float32Array","ScoreAudio","Audio","require","PlopAudio","StartAudio","SideAudio","GameStage","Welcome","Playing","P1Win","P2Win","Pong","renderer","canvas","stage","countdown","time","maxY","state","State","center","setTime","transition","syncState","setGameStage","draw","currentStage","updatePlayerState","P1","P2","play","updateBallState","ball","newState","terminating","maxBounceAngle","PI","left","right","bottom","top","distanceToNormal","normalizedDistanceToNormal","bounceAngle","cos","sin","setLocation","abs","P","min","max","setOldPositions","setLeftPaddlePosition","setRightPaddlePosition","setBallPosition","setScore","addEventListener","hitboxSize","click","getCursorPosition","k","code","rect","getBoundingClientRect","clientX","clientY","Shader","gl","vsSource","fsSource","program","initShaderProgram","vertexShader","loadShader","VERTEX_SHADER","fragmentShader","FRAGMENT_SHADER","shaderProgram","createProgram","attachShader","linkProgram","getProgramParameter","LINK_STATUS","alert","getProgramInfoLog","source","sourceVars","sourceVarPattern","replace","_","varName","shader","createShader","shaderSource","compileShader","getShaderParameter","COMPILE_STATUS","getShaderInfoLog","deleteShader","useProgram","uniform1f","getUniformLocation","uniformMatrix4fv","uniform2fv","uniform3fv","getAttribLocation","deleteProgram","PongRenderer","buffers","initBuffers","use","setDefaultPositions","shaderSourceVars","delete","positionBuffer","createBuffer","bindBuffer","ARRAY_BUFFER","positions","bufferData","STATIC_DRAW","position","clearColor","clear","COLOR_BUFFER_BIT","clearDepth","enable","DEPTH_TEST","depthFunc","LEQUAL","DEPTH_BUFFER_BIT","fieldOfView","aspect","clientWidth","clientHeight","zNear","zFar","projectionMatrix","mat4","perspective","modelViewMatrix","translate","vertexPositionLocation","vertexAttribPointer","FLOAT","enableVertexAttribArray","setUniformMatrix4fv","offset","vertexCount","drawArrays","TRIANGLE_STRIP","entries","pos","setUniform2fv","setUniform1f","gameStage","pong","pongRenderer","mounted","addEventListeners","requestAnimationFrame","render","Vue","VueRouter","routes","path","Home","router","base","Vuex","Store","mutations","actions","config","productionTip","store","h","App","$mount"],"mappings":"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","file":"js/app.4b46cf16.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t\"app\": 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"js/\" + ({\"about\":\"about\"}[chunkId]||chunkId) + \".\" + {\"about\":\"c058c680\"}[chunkId] + \".js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/infogr-pong/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// add entry module to deferred list\n \tdeferredModules.push([0,\"chunk-vendors\"]);\n \t// run deferred modules when ready\n \treturn checkDeferredModules();\n","import mod from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=style&index=0&lang=scss&\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{\"id\":\"app\"}},[_c('router-view')],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./App.vue?vue&type=template&id=50d778a2&\"\nvar script = {}\nimport style0 from \"./App.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","/* eslint-disable no-console */\n\nimport { register } from 'register-service-worker'\n\nif (process.env.NODE_ENV === 'production') {\n register(`${process.env.BASE_URL}service-worker.js`, {\n ready () {\n console.log(\n 'App is being served from cache by a service worker.\\n' +\n 'For more details, visit https://goo.gl/AFskqB'\n )\n },\n registered () {\n console.log('Service worker has been registered.')\n },\n cached () {\n console.log('Content has been cached for offline use.')\n },\n updatefound () {\n console.log('New content is downloading.')\n },\n updated () {\n console.log('New content is available; please refresh.')\n },\n offline () {\n console.log('No internet connection found. App is running in offline mode.')\n },\n error (error) {\n console.error('Error during service worker registration:', error)\n }\n })\n}\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"home\"},[_c('div',{staticClass:\"canvas-wrapper\"},[_c('canvas',{attrs:{\"id\":\"glCanvas\",\"width\":\"1200\",\"height\":\"800\"}})])])}]\n\nexport { render, staticRenderFns }","const Movement = {\n None: 0,\n Up: 1,\n Down: 2\n}\n\nexport default Movement\n","import Movement from './movement'\n\nexport default class Player {\n width\n height\n x\n y\n direction\n score\n speed\n\n constructor (x) {\n this.width = 0.04\n this.height = 0.3\n this.x = x\n this.y = 0\n this.speed = 0.02\n this.direction = Movement.None\n this.score = 0\n }\n\n left () {\n return this.x - this.width / 2\n }\n\n right () {\n return this.x + this.width / 2\n }\n\n top () {\n return this.y + this.height / 2\n }\n\n bottom () {\n return this.y - this.height / 2\n }\n}\n","export default class Ball {\n radius\n x\n y\n direction\n oldPositions = []\n speed\n acceleration\n\n startDirections = [\n { x: 0.01, y: 0.01 },\n { x: 0.01, y: -0.01 },\n { x: -0.01, y: 0.01 },\n { x: -0.01, y: -0.01 }\n ]\n\n setLocation (x, y) {\n this.oldPositions.push(new Float32Array([x, y]))\n if (this.oldPositions.length > 30) {\n this.oldPositions.shift()\n }\n this.x = x\n this.y = y\n }\n\n constructor () {\n this.radius = 0.01\n this.x = 0\n this.y = 0\n this.direction = this.startDirections[Math.floor(Math.random() * this.startDirections.length)]\n this.speed = 0.01\n this.acceleration = 1.1\n\n for (var i = 0; i < 30; i++) {\n this.oldPositions[i] = new Float32Array([0, 0])\n }\n }\n\n left () {\n return this.x - this.radius\n }\n\n right () {\n return this.x + this.radius\n }\n\n top () {\n return this.y + this.radius\n }\n\n bottom () {\n return this.y - this.radius\n }\n}\n","import Player from './player'\nimport Movement from './movement'\nimport Ball from './ball'\n\nconst ScoreAudio = new Audio(require('../assets/score.wav'))\nconst PlopAudio = new Audio(require('../assets/hit.wav'))\nconst StartAudio = new Audio(require('../assets/start.wav'))\nconst SideAudio = new Audio(require('../assets/side.wav'))\n\nexport const GameStage = {\n Welcome: 0.0,\n Playing: 1.0,\n P1Win: 2.0,\n P2Win: 3.0\n}\n\nexport class Pong {\n countdown\n stage\n canvas\n center\n renderer\n state\n maxY\n time\n\n constructor (renderer, canvas, stage) {\n this.countdown = 60\n this.time = 0\n if (typeof stage !== 'undefined') {\n this.stage = stage\n } else {\n this.stage = GameStage.Playing\n }\n\n this.canvas = canvas\n this.renderer = renderer\n this.maxY = 0.65\n this.state = new State()\n\n this.center = {\n x: canvas.width / 2,\n y: canvas.height / 2\n }\n }\n\n tick () {\n this.time++\n this.renderer.setTime(this.time)\n if (this.stage === GameStage.Playing) {\n this.state = this.transition(this.state)\n this.syncState()\n } else {\n this.renderer.setGameStage(this.stage)\n }\n this.draw()\n }\n\n transition (state) {\n this.countdown--\n this.stage = this.state.currentStage()\n this.updatePlayerState(state.P1)\n this.updatePlayerState(state.P2)\n\n if (this.countdown > 0) {\n return state\n }\n\n if (this.countdown === 0) {\n StartAudio.play()\n }\n\n this.updateBallState(state.ball)\n\n if (state.ball.x <= -1) {\n const newState = new State()\n newState.P1 = state.P1\n newState.P2 = state.P2\n newState.P2.score++\n ScoreAudio.play()\n this.countdown = 60\n return newState\n } else if (state.ball.x >= 1) {\n const newState = new State()\n newState.P1 = state.P1\n newState.P2 = state.P2\n newState.P1.score++\n this.countdown = 60\n ScoreAudio.play()\n return newState\n }\n\n if (state.terminating) {\n return state\n }\n\n const maxBounceAngle = 5 * Math.PI / 12\n\n // Paddle collision\n if (state.ball.left() < state.P1.right()) {\n if ((state.ball.bottom() < state.P1.top() && state.ball.bottom > state.P1.bottom()) ||\n (state.ball.top() < state.P1.top() && state.ball.top() > state.P1.bottom())) {\n PlopAudio.play()\n const distanceToNormal = state.P1.y - state.ball.y\n const normalizedDistanceToNormal = distanceToNormal / (state.P1.height / 2)\n const bounceAngle = normalizedDistanceToNormal * maxBounceAngle\n\n state.ball.speed *= state.ball.acceleration\n state.ball.direction.x = state.ball.speed * Math.cos(bounceAngle)\n state.ball.direction.y = -state.ball.speed * Math.sin(bounceAngle)\n } else {\n state.terminating = true\n }\n }\n\n if (state.ball.right() > state.P2.left()) {\n if ((state.ball.bottom() < state.P2.top() && state.ball.bottom > state.P2.bottom()) ||\n (state.ball.top() < state.P2.top() && state.ball.top() > state.P2.bottom())) {\n PlopAudio.play()\n const distanceToNormal = state.P2.y - state.ball.y\n const normalizedDistanceToNormal = distanceToNormal / (state.P2.height / 2)\n const bounceAngle = normalizedDistanceToNormal * maxBounceAngle\n\n state.ball.speed *= state.ball.acceleration\n\n state.ball.direction.x = state.ball.speed * -Math.cos(bounceAngle)\n state.ball.direction.y = state.ball.speed * -Math.sin(bounceAngle)\n } else {\n state.terminating = true\n }\n }\n\n // if ball hits paddle, compute new ball direction.\n return this.state\n }\n\n updateBallState (ball) {\n ball.setLocation(ball.x + ball.direction.x, ball.y + ball.direction.y)\n if (Math.abs(ball.y) > this.maxY) {\n SideAudio.play()\n ball.direction.y *= -1\n }\n }\n\n updatePlayerState (P) {\n switch (P.direction) {\n case Movement.Up:\n P.y = Math.min(P.y + P.speed, this.maxY - P.height / 2)\n break\n case Movement.Down:\n P.y = Math.max(P.y - P.speed, -this.maxY + P.height / 2)\n break\n }\n }\n\n syncState () {\n this.renderer.setOldPositions(this.state.ball.oldPositions)\n this.renderer.setGameStage(this.stage)\n this.renderer.setLeftPaddlePosition(this.state.P1.x, this.state.P1.y)\n this.renderer.setRightPaddlePosition(this.state.P2.x, this.state.P2.y)\n this.renderer.setBallPosition(this.state.ball.x, this.state.ball.y)\n this.renderer.setScore(this.state.P1.score, this.state.P2.score)\n }\n\n draw () {\n this.renderer.draw()\n }\n\n addEventListeners () {\n this.canvas.addEventListener('mousedown', (e) => {\n const hitboxSize = 50\n const click = getCursorPosition(this.canvas, e)\n\n if (Math.abs(this.center.x - click.x) < hitboxSize &&\n Math.abs(this.center.y - click.y) < hitboxSize &&\n this.stage !== GameStage.Playing) {\n this.state = new State()\n this.stage = GameStage.Playing\n }\n })\n\n document.addEventListener('keydown', (k) => {\n switch (k.code) {\n case 'ArrowUp':\n this.state.P2.direction = Movement.Up\n break\n case 'ArrowDown':\n this.state.P2.direction = Movement.Down\n break\n case 'KeyW':\n this.state.P1.direction = Movement.Up\n break\n case 'KeyS':\n this.state.P1.direction = Movement.Down\n break\n }\n })\n\n document.addEventListener('keyup', (k) => {\n switch (k.code) {\n case 'ArrowUp':\n if (this.state.P2.direction === Movement.Up) {\n this.state.P2.direction = Movement.None\n }\n break\n case 'ArrowDown':\n if (this.state.P2.direction === Movement.Down) {\n this.state.P2.direction = Movement.None\n }\n break\n case 'KeyW':\n if (this.state.P1.direction === Movement.Up) {\n this.state.P1.direction = Movement.None\n }\n break\n case 'KeyS':\n if (this.state.P1.direction === Movement.Down) {\n this.state.P1.direction = Movement.None\n }\n break\n }\n })\n }\n}\n\n// https://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element\nfunction getCursorPosition (canvas, event) {\n const rect = canvas.getBoundingClientRect()\n const x = event.clientX - rect.left\n const y = event.clientY - rect.top\n return { x: x, y: y }\n}\n\nclass State {\n P1\n P2\n ball\n terminating\n\n constructor () {\n this.ball = new Ball()\n this.P1 = new Player(-0.9)\n this.P2 = new Player(0.9)\n this.terminating = false\n }\n\n gameOver () {\n return this.P1.score > 9 || this.P2.score > 9\n }\n\n currentStage () {\n if (this.P1.score >= 9) {\n return GameStage.P1Win\n } else if (this.P2.score >= 9) {\n return GameStage.P2Win\n }\n return GameStage.Playing\n }\n}\n","export default class Shader {\n gl\n program\n\n // constructor (gl, vsSource, fsSource, sourceVars) {\n constructor (gl, vsSource, fsSource) {\n this.gl = gl\n\n // preprocess sources\n // vsSource = this.preprocessSource(vsSource, sourceVars)\n // fsSource = this.preprocessSource(fsSource, sourceVars)\n\n this.program = this.initShaderProgram(gl, vsSource, fsSource)\n }\n\n //\n // Initialize a shader program, so WebGL knows how to draw our data\n //\n initShaderProgram (gl, vsSource, fsSource) {\n const vertexShader = this.loadShader(gl, gl.VERTEX_SHADER, vsSource)\n const fragmentShader = this.loadShader(gl, gl.FRAGMENT_SHADER, fsSource)\n\n // Create the shader program\n const shaderProgram = gl.createProgram()\n gl.attachShader(shaderProgram, vertexShader)\n gl.attachShader(shaderProgram, fragmentShader)\n gl.linkProgram(shaderProgram)\n\n // If creating the shader program failed, alert\n if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {\n alert(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`)\n return null\n }\n\n return shaderProgram\n }\n\n //\n // preprocesses our shader source files\n // it replaces variables in the format 42//$VARIABLE_NAME$// with the VARIABLE_NAME\n // item from the sourceVars object\n //\n preprocessSource (source, sourceVars) {\n const sourceVarPattern = /42\\/\\/\\$(.*)\\$\\/\\//g\n return source.replace(sourceVarPattern, (_, varName) => sourceVars[varName])\n }\n\n //\n // creates a shader of the given type, uploads the source and\n // compiles it.\n //\n loadShader (gl, type, source) {\n const shader = gl.createShader(type)\n\n // Send the source to the shader object\n gl.shaderSource(shader, source)\n\n // Compile the shader program\n gl.compileShader(shader)\n\n // See if it compiled successfully\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(`An error occurred compiling the shaders:\\n${gl.getShaderInfoLog(shader)}`)\n gl.deleteShader(shader)\n return null\n }\n\n return shader\n }\n\n use () {\n this.gl.useProgram(this.program)\n }\n\n setUniform1f (name, value) {\n this.gl.uniform1f(this.getUniformLocation(name), value)\n }\n\n setUniformMatrix4fv (name, value) {\n this.gl.uniformMatrix4fv(this.getUniformLocation(name), false, value)\n }\n\n setUniform2fv (name, value) {\n this.gl.uniform2fv(this.getUniformLocation(name), value)\n }\n\n setUniform3fv (name, value) {\n this.gl.uniform3fv(this.getUniformLocation(name), value)\n }\n\n getUniformLocation (name) {\n // TODO: Cache uniform locations\n return this.gl.getUniformLocation(this.program, name)\n }\n\n getAttribLocation (name) {\n // TODO: Cache attrib locations\n return this.gl.getAttribLocation(this.program, name)\n }\n\n delete () {\n this.gl.deleteProgram(this.program)\n }\n}\n","export default \"precision highp float;\\n\\nvarying mediump vec2 screenPosition;\\n\\n//\\n// GAME STATE\\n//\\nuniform vec2 score;\\nuniform vec2 ballPosition;\\nuniform vec2 leftPaddlePosition;\\nuniform vec2 rightPaddlePosition;\\nuniform float gameStage;\\nuniform vec2 oldPositions[30];\\nuniform float time;\\n\\n//\\n// CURVE\\n//\\nvec2 curveAmount = vec2(0.8, 0.6);\\nfloat caseBorder = 0.0125;\\n\\n\\nfloat ballRadius = 0.02;\\nvec2 paddleSize = vec2(0.04, 0.24);\\n\\n\\n//\\n// DRAWING FUNCTIONS\\n//\\nbool isInRect(vec2 position, vec2 center, vec2 size)\\n{\\n vec2 delta = abs(center - position); // delta for paddle 0\\n return delta.x < (size.x/2.0) && delta.y < (size.y/2.0);\\n}\\n\\nfloat pixelSize = 0.03;\\n\\n// xxx\\n// x x\\n// x x\\n// x x\\n// xxx\\nbool is0(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\\n}\\n\\n\\n// x\\n// x\\n// x\\n// x\\n// x\\nbool is1(vec2 position, vec2 pos)\\n{\\n return isInRect(position, pos, vec2(pixelSize, pixelSize * 5.0));\\n}\\n\\n// xxx\\n// x\\n// xxx\\n// x\\n// xxx\\nbool is2(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));\\n}\\n\\n// xxx\\n// x\\n// xxx\\n// x\\n// xxx\\nbool is3(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 3.0 * pixelSize));\\n}\\n\\n// x x\\n// x x\\n// xxx\\n// x\\n// x\\nbool is4(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||\\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize));\\n}\\n\\n// xxx\\n// x\\n// xxx\\n// x\\n// xxx\\nbool is5(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, pos, vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize));\\n}\\n\\n// xxx\\n// x\\n// xxx\\n// x x\\n// xxx\\nbool is6(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0)) ||\\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y - 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0));\\n}\\n\\n// xxx\\n// x\\n// x\\n// x\\n// x\\nbool is7(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\\n}\\n\\n// xxx\\n// x x\\n// xxx\\n// x x\\n// xxx\\nbool is8(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||\\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0));\\n}\\n\\n// xxx\\n// x x\\n// xxx\\n// x\\n// xxx\\nbool is9(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - 1.0 * pixelSize, pos.y + 1.0 * pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||\\n isInRect(position, vec2(pos.x, pos.y), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x + 1.0 * pixelSize, pos.y), vec2(pixelSize, pixelSize * 4.0));\\n}\\n\\n// xxx\\n// x x\\n// xxx\\n// x\\n// x\\nbool isP(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\\n isInRect(position, pos, vec2(pixelSize, pixelSize)) ||\\n isInRect(position, vec2(pos.x + pixelSize, pos.y + pixelSize), vec2(pixelSize, 3.0 * pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize, pixelSize));\\n}\\n\\nbool isO(vec2 position, vec2 pos)\\n{\\n return is0(position, pos);\\n}\\n\\n// xxx\\n// x x\\n// x x\\n// x x\\n// x x\\nbool isN(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\\n isInRect(position, vec2(pos.x + pixelSize, pos.y), vec2(pixelSize, 5.0 * pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(3.0 * pixelSize, pixelSize));\\n}\\n\\n\\n// xxx\\n// x\\n// x|x\\n// x x\\n// xxx\\nbool isG(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize * 3.0, pixelSize)) ||\\n isInRect(position, vec2(pos.x - pixelSize, pos.y), vec2(pixelSize, pixelSize * 3.0)) ||\\n isInRect(position, vec2(pos.x + pixelSize, pos.y - pixelSize), vec2(pixelSize)) ||\\n isInRect(position, vec2(pos.x + 0.75 * pixelSize, pos.y), vec2(1.5 * pixelSize, pixelSize));\\n}\\n\\n// x\\n// x\\n// x\\n//\\n// x\\nbool isExclamationMark(vec2 position, vec2 pos)\\n{\\n return\\n isInRect(position, vec2(pos.x, pos.y + pixelSize), vec2(pixelSize, pixelSize * 3.0)) ||\\n isInRect(position, vec2(pos.x, pos.y - 2.0 * pixelSize), vec2(pixelSize));\\n}\\n\\n\\nbool isPONG(vec2 position, vec2 pos)\\n{\\n return\\n isP(position, vec2(pos.x - 6.0 * pixelSize, pos.y)) ||\\n isO(position, vec2(pos.x - 2.0 * pixelSize, pos.y)) ||\\n isN(position, vec2(pos.x + 2.0 * pixelSize, pos.y)) ||\\n isG(position, vec2(pos.x + 6.0 * pixelSize, pos.y));\\n}\\n\\n\\nbool isTriangle(vec2 position, vec2 size, vec2 pos)\\n{\\n float top = pos.y + size.y;\\n float bottom = pos.y - size.y;\\n float start = pos.x;\\n float end = pos.x + size.x;\\n\\n float slope = size.y/size.x;\\n float relativeX = position.x - start;\\n\\n return\\n position.x > start && position.x < end && // x within triangle\\n (position.y < top - slope * relativeX && position.y > bottom + slope * relativeX); // y within triangle\\n\\n}\\n\\nbool isNumber(float number, vec2 position, vec2 numberPosition)\\n{ // GLSL is a beautiful language\\n if (number == 0.0) { return is0(position, numberPosition); }\\n if (number == 1.0) { return is1(position, numberPosition); }\\n if (number == 2.0) { return is2(position, numberPosition); }\\n if (number == 3.0) { return is3(position, numberPosition); }\\n if (number == 4.0) { return is4(position, numberPosition); }\\n if (number == 5.0) { return is5(position, numberPosition); }\\n if (number == 6.0) { return is6(position, numberPosition); }\\n if (number == 7.0) { return is7(position, numberPosition); }\\n if (number == 8.0) { return is8(position, numberPosition); }\\n if (number == 9.0) { return is9(position, numberPosition); }\\n return false;\\n}\\n\\nbool isP1score(vec2 position) {\\n return isNumber(score.x, position, vec2(-4.0 * pixelSize, 18.0 * pixelSize));\\n}\\n\\nbool isP2score(vec2 position) {\\n return isNumber(score.y, position, vec2(4.0 * pixelSize, 18.0 * pixelSize));\\n}\\n\\nbool isLine(vec2 position) {\\n return\\n abs(position.x) < pixelSize / 2.0 &&\\n mod(position.y - pixelSize * 1.5, pixelSize * 2.0) <= pixelSize;\\n}\\n\\nbool isWinLine(vec2 position) {\\n return isLine(position) &&\\n (position.y > pixelSize * 3.0 || position.y < pixelSize * -3.0);\\n}\\n\\nbool isPlayButton(vec2 position) {\\n return isTriangle(position, vec2(0.16, 0.1), vec2(-0.06, 0));\\n}\\n\\n//\\n// GAMESTAGE SPECIFIC RENDERING\\n//\\nvec4 welcome(vec2 position) { // GameStage.Welcome\\n if (isPONG(position, vec2(0, 0.3)) ||\\n isPlayButton(position))\\n {\\n return vec4(1, 1, 1, 1);\\n }\\n return vec4(0, 0, 0, 1.0);\\n}\\n\\nvec4 playing(vec2 position) // GameStage.Playing\\n{\\n bool isLeftPaddle = isInRect(position, leftPaddlePosition, paddleSize);\\n bool isRightPaddle = isInRect(position, rightPaddlePosition, paddleSize);\\n bool isBall = isInRect(position, ballPosition, vec2(ballRadius * 2.0, ballRadius * 2.0));\\n\\n if (isLeftPaddle ||\\n isRightPaddle ||\\n isBall ||\\n isLine(position) ||\\n isP1score(position) ||\\n isP2score(position))\\n {\\n return vec4(1, 1, 1, 1);\\n }\\n\\n // Render tail\\n for (int i = 29; i >= 0; i--) {\\n vec2 oldPos = oldPositions[i];\\n float scale = float(i) / 30.0;\\n if (isInRect(position, oldPos, vec2(ballRadius * 2.0 * scale))) {\\n return vec4(scale, scale, scale, 1.0);\\n }\\n }\\n\\n return vec4(0, 0, 0, 1.0);\\n}\\n\\nvec4 p1win(vec2 position) // GameStage.P1Win\\n{\\n if (isP(position, vec2(-19.0 * pixelSize, 0)) ||\\n is1(position, vec2(-16.0 * pixelSize, 0)) ||\\n isExclamationMark(position, vec2(-14.0 * pixelSize, 0)) ||\\n isP1score(position) ||\\n isP2score(position) ||\\n isWinLine(position) ||\\n isPlayButton(position)\\n )\\n {\\n return vec4(1, 1, 1, 1);\\n }\\n return vec4(0, 0, 0, 1.0);\\n}\\n\\nvec4 p2win(vec2 position) // GameStage.P2Win\\n{\\n if (isP(position, vec2(14.0 * pixelSize, 0)) ||\\n is2(position, vec2(18.0 * pixelSize, 0)) ||\\n isExclamationMark(position, vec2(21.0 * pixelSize, 0)) ||\\n isP1score(position) ||\\n isP2score(position) ||\\n isWinLine(position) ||\\n isPlayButton(position)\\n )\\n {\\n return vec4(1, 1, 1, 1);\\n }\\n return vec4(0, 0, 0, 1.0);\\n}\\n\\nvec4 scanline(vec4 color, vec2 position) {\\n float lineHeight = 0.0104;\\n float darkness = 0.9;\\n float speed = 0.0104;\\n\\n float y = mod(position.y + (time / 60.0) * speed, lineHeight);\\n if (y < lineHeight/2.0) {\\n return color;\\n } else {\\n return vec4(color.x, color.y, color.z, darkness);\\n }\\n}\\n\\nvec4 colorAt(vec2 position) {\\n // Map position from [0, 1] to [-1, 1] to get world space color\\n position *= 2.0;\\n position -= vec2(1.0);\\n\\n vec4 color;\\n\\n if (abs(position.y) > 0.666) { // out of render area\\n return vec4(0, 0, 0, 1);\\n }\\n\\n if (gameStage == 0.0) { // GameStage.Welcome\\n color = welcome(position);\\n } else if (gameStage == 1.0) { // GameStage.Playing\\n color = playing(position);\\n } else if (gameStage == 2.0) { // GameStage.P1Win\\n color = p1win(position);\\n } else if (gameStage == 3.0) { // GameStage.P2Win\\n color = p2win(position);\\n }\\n\\n // Apply scanline effect\\n color = scanline(color, position);\\n\\n return color;\\n}\\n\\n\\nvec4 curve(vec2 position) {\\n // Map position from [-1, 1] to [0, 1] for curve effect\\n position += vec2(1.0);\\n position /= 2.0;\\n\\n // Using this https://github.com/wessles/GLSL-CRT/blob/master/shader.frag algorithm\\n float dx = abs(0.5 - position.x);\\n float dy = abs(0.5 - position.y);\\n dx *= dx;\\n dy *= dy;\\n\\n position.x -= 0.5;\\n position.x *= 1.0 + (dy * curveAmount.x);\\n position.x += 0.5;\\n\\n position.y -= 0.5;\\n position.y *= 1.0 + (dx * curveAmount.y);\\n position.y += 0.5;\\n\\n // Draw color from world space\\n vec4 color = colorAt(position);\\n color += sin(position.y) * 0.02;\\n\\n if(position.y > 1.0 || position.x < 0.0 || position.x > 1.0 || position.y < 0.0)\\n color = vec4(0, 0, 0, 1);\\n\\n return color;\\n}\\n\\nvoid main() {\\n gl_FragColor = curve(screenPosition);\\n}\\n\";","export default \"precision highp float;\\n\\nattribute vec4 aVertexPosition;\\n\\nuniform mat4 uModelViewMatrix;\\nuniform mat4 uProjectionMatrix;\\n\\nvarying mediump vec2 screenPosition;\\n\\nvoid main() {\\n screenPosition = aVertexPosition.xy;\\n gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;\\n}\\n\";","import { mat4 } from 'gl-matrix'\nimport Shader from './shader'\n\nimport fsSource from './shaders/shader.frag'\nimport vsSource from './shaders/shader.vert'\n\nexport default class PongRenderer {\n gl\n shader\n buffers\n\n // constructor (gl, shaderSourceVars) {\n constructor (gl) {\n this.gl = gl\n this.shader = new Shader(gl, vsSource, fsSource)\n this.buffers = this.initBuffers()\n\n this.shader.use()\n this.setDefaultPositions()\n }\n\n recompileShader (shaderSourceVars) {\n this.shader.delete()\n this.shader = new Shader(this.gl, vsSource, fsSource, shaderSourceVars)\n }\n\n initBuffers () {\n // Create a buffer for the square's positions.\n\n const positionBuffer = this.gl.createBuffer()\n\n // Select the positionBuffer as the one to apply buffer\n // operations to from here out.\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer)\n\n // Now create an array of positions for the square.\n\n const positions = [\n -1.0, 1.0,\n 1.0, 1.0,\n -1.0, -1.0,\n 1.0, -1.0\n ]\n\n // Now pass the list of positions into WebGL to build the\n // shape. We do this by creating a Float32Array from the\n // JavaScript array, then use it to fill the current buffer.\n this.gl.bufferData(this.gl.ARRAY_BUFFER,\n new Float32Array(positions),\n this.gl.STATIC_DRAW)\n\n return {\n position: positionBuffer\n }\n }\n\n draw () {\n this.gl.clearColor(0.0, 0.0, 0.0, 1.0) // Clear to black, fully opaque\n this.gl.clear(this.gl.COLOR_BUFFER_BIT)\n this.gl.clearDepth(1.0) // Clear everything\n this.gl.enable(this.gl.DEPTH_TEST) // Enable depth testing\n this.gl.depthFunc(this.gl.LEQUAL) // Near things obscure far things\n\n // Clear the canvas before we start drawing on it.\n\n this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)\n\n // Create a perspective matrix, a special matrix that is\n // used to simulate the distortion of perspective in a camera.\n const fieldOfView = 29.8 * Math.PI / 180 // in radians\n const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight\n const zNear = 0.1\n const zFar = 100.0\n const projectionMatrix = mat4.create()\n\n // gl-matrix.js always has the first argument as the destination to receive the result.\n mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar)\n\n // Set the drawing position to the \"identity\" point, which is\n // the center of the scene.\n const modelViewMatrix = mat4.create()\n\n // Now move the drawing position a bit to where we want to\n // start drawing the square.\n mat4.translate(modelViewMatrix, // destination matrix\n modelViewMatrix, // matrix to translate\n [0.0, 0.0, -2.5]) // amount to translate\n\n // Tell WebGL how to pull out the positions from the position\n // buffer into the vertexPosition attribute.\n const vertexPositionLocation = this.shader.getAttribLocation('aVertexPosition')\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.position)\n this.gl.vertexAttribPointer(vertexPositionLocation, 2, this.gl.FLOAT, false, 0, 0)\n this.gl.enableVertexAttribArray(vertexPositionLocation)\n\n // Tell WebGL to use our program when drawing\n this.shader.use()\n\n // Set the shader uniforms\n this.shader.setUniformMatrix4fv('uProjectionMatrix', projectionMatrix)\n this.shader.setUniformMatrix4fv('uModelViewMatrix', modelViewMatrix)\n {\n const offset = 0\n const vertexCount = 4\n this.gl.drawArrays(this.gl.TRIANGLE_STRIP, offset, vertexCount)\n }\n }\n\n setOldPositions (oldPositions) {\n for (const [i, pos] of oldPositions.entries()) {\n this.shader.setUniform2fv(`oldPositions[${i}]`, pos)\n }\n }\n\n setTime (time) {\n this.shader.setUniform1f('time', time)\n }\n\n setGameStage (gameStage) {\n this.shader.setUniform1f('gameStage', gameStage)\n }\n\n setDefaultPositions () {\n this.setLeftPaddlePosition(-0.9, 0)\n this.setRightPaddlePosition(0.9, 0)\n this.setBallPosition(0, 0)\n }\n\n setLeftPaddlePosition (x, y) {\n this.shader.setUniform2fv('leftPaddlePosition', new Float32Array([x, y]))\n }\n\n setRightPaddlePosition (x, y) {\n this.shader.setUniform2fv('rightPaddlePosition', new Float32Array([x, y]))\n }\n\n setBallPosition (x, y) {\n this.shader.setUniform2fv('ballPosition', new Float32Array([x, y]))\n }\n\n setScore (x, y) {\n this.shader.setUniform2fv('score', new Float32Array([x, y]))\n }\n}\n","\n \n\n\n\n\n\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Home.vue?vue&type=template&id=6ea6888c&\"\nimport script from \"./Home.vue?vue&type=script&lang=js&\"\nexport * from \"./Home.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Home.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport Home from '../views/Home.vue'\n\nVue.use(VueRouter)\n\nconst routes = [\n {\n path: '/',\n name: 'Home',\n component: Home\n },\n {\n path: '/about',\n name: 'About',\n // route level code-splitting\n // this generates a separate chunk (about.[hash].js) for this route\n // which is lazy-loaded when the route is visited.\n component: () => import(/* webpackChunkName: \"about\" */ '../views/About.vue')\n }\n]\n\nconst router = new VueRouter({\n mode: 'history',\n base: process.env.BASE_URL,\n routes\n})\n\nexport default router\n","import Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n state: {\n },\n mutations: {\n },\n actions: {\n },\n modules: {\n }\n})\n","import Vue from 'vue'\nimport App from './App.vue'\nimport './registerServiceWorker'\nimport router from './router'\nimport store from './store'\n\nVue.config.productionTip = false\n\nnew Vue({\n router,\n store,\n render: h => h(App)\n}).$mount('#app')\n","import mod from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\"","module.exports = __webpack_public_path__ + \"media/score.63ce9ecd.wav\";","module.exports = __webpack_public_path__ + \"media/hit.f8648124.wav\";","module.exports = __webpack_public_path__ + \"media/side.f37fcc5f.wav\";","module.exports = __webpack_public_path__ + \"media/start.1f4848ab.wav\";"],"sourceRoot":""}
--------------------------------------------------------------------------------