├── .babelrc
├── .eslintrc.js
├── .gitignore
├── README.md
├── favicon.ico
├── package-lock.json
├── package.json
├── src
├── audio
│ ├── audioCompleteLevel.mp3
│ ├── audioDescend.mp3
│ ├── audioDie.mp3
│ ├── audioFireFlowerShot.mp3
│ ├── audioFireworkBurst.mp3
│ ├── audioFireworkWhistle.mp3
│ ├── audioGameOver.mp3
│ ├── audioGoombaSquash.mp3
│ ├── audioJump.mp3
│ ├── audioLosePowerUp.mp3
│ ├── audioMusicLevel1.mp3
│ └── audioWinLevel.mp3
├── img
│ ├── background.png
│ ├── block.png
│ ├── blockTri.png
│ ├── flagPole.png
│ ├── hills.png
│ ├── level2
│ │ ├── background.png
│ │ ├── lgPlatform.png
│ │ ├── mdPlatform.png
│ │ ├── mountain.png
│ │ ├── mountains.png
│ │ └── sun.png
│ ├── lgPlatform.png
│ ├── mario
│ │ ├── spriteFireFlowerShootLeft.png
│ │ └── spriteFireFlowerShootRight.png
│ ├── mdPlatform.png
│ ├── platform.png
│ ├── platformSmallTall.png
│ ├── spriteFireFlower.png
│ ├── spriteFireFlowerJumpLeft.png
│ ├── spriteFireFlowerJumpRight.png
│ ├── spriteFireFlowerRunLeft.png
│ ├── spriteFireFlowerRunRight.png
│ ├── spriteFireFlowerStandLeft.png
│ ├── spriteFireFlowerStandRight.png
│ ├── spriteGoomba.png
│ ├── spriteMarioJumpLeft.png
│ ├── spriteMarioJumpRight.png
│ ├── spriteMarioRunLeft.png
│ ├── spriteMarioRunRight.png
│ ├── spriteMarioStandLeft.png
│ ├── spriteMarioStandRight.png
│ ├── spriteRunLeft.png
│ ├── spriteRunRight.png
│ ├── spriteStandLeft.png
│ ├── spriteStandRight.png
│ ├── tPlatform.png
│ └── xtPlatform.png
├── index.html
└── js
│ ├── audio.js
│ ├── canvas.js
│ ├── images.js
│ └── utils.js
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": [
4 | ["@babel/plugin-transform-runtime"]
5 | ]
6 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true
5 | },
6 | extends: 'eslint:recommended',
7 | parserOptions: {
8 | sourceType: 'module'
9 | },
10 | rules: {
11 | indent: ['error', 4],
12 | 'linebreak-style': ['error', 'unix'],
13 | quotes: ['error', 'single'],
14 | semi: ['error', 'never']
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .ds_store
3 | dist
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Canvas Boilerplate is the go-to solution for quickly creating modern canvas pieces using ES6 and webpack.
2 |
3 | ## Getting Started
4 |
5 | 1. Clone the repo:
6 |
7 | git clone https://github.com/christopher4lis/canvas-boilerplate.git
8 |
9 | 2. Install dependencies:
10 |
11 | yarn
12 |
13 | or
14 |
15 | npm install
16 |
17 | 3. Run webpack:
18 |
19 | npm start
20 |
21 | Your canvas piece should open up automatically at http://localhost:3000 and you should see 'HTML CANVAS BOILERPLATE' on hover.
22 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/favicon.ico
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "canvas-template",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "webpack.config.js",
6 | "scripts": {
7 | "dev": "webpack --config webpack.config.js",
8 | "start": "webpack --config webpack.config.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "@babel/runtime": "^7.16.7",
16 | "gsap": "^3.9.1",
17 | "howler": "^2.2.3"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.9.0",
21 | "@babel/plugin-transform-runtime": "^7.16.7",
22 | "@babel/preset-env": "^7.9.0",
23 | "babel-loader": "^8.1.0",
24 | "browser-sync": "^2.26.7",
25 | "browser-sync-webpack-plugin": "^2.2.2",
26 | "file-loader": "^6.2.0",
27 | "html-webpack-plugin": "^4.0.3",
28 | "prettier": "^2.0.2",
29 | "webpack": "^4.42.1",
30 | "webpack-cli": "^3.3.11"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/audio/audioCompleteLevel.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioCompleteLevel.mp3
--------------------------------------------------------------------------------
/src/audio/audioDescend.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioDescend.mp3
--------------------------------------------------------------------------------
/src/audio/audioDie.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioDie.mp3
--------------------------------------------------------------------------------
/src/audio/audioFireFlowerShot.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioFireFlowerShot.mp3
--------------------------------------------------------------------------------
/src/audio/audioFireworkBurst.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioFireworkBurst.mp3
--------------------------------------------------------------------------------
/src/audio/audioFireworkWhistle.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioFireworkWhistle.mp3
--------------------------------------------------------------------------------
/src/audio/audioGameOver.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioGameOver.mp3
--------------------------------------------------------------------------------
/src/audio/audioGoombaSquash.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioGoombaSquash.mp3
--------------------------------------------------------------------------------
/src/audio/audioJump.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioJump.mp3
--------------------------------------------------------------------------------
/src/audio/audioLosePowerUp.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioLosePowerUp.mp3
--------------------------------------------------------------------------------
/src/audio/audioMusicLevel1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioMusicLevel1.mp3
--------------------------------------------------------------------------------
/src/audio/audioWinLevel.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/audio/audioWinLevel.mp3
--------------------------------------------------------------------------------
/src/img/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/background.png
--------------------------------------------------------------------------------
/src/img/block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/block.png
--------------------------------------------------------------------------------
/src/img/blockTri.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/blockTri.png
--------------------------------------------------------------------------------
/src/img/flagPole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/flagPole.png
--------------------------------------------------------------------------------
/src/img/hills.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/hills.png
--------------------------------------------------------------------------------
/src/img/level2/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/background.png
--------------------------------------------------------------------------------
/src/img/level2/lgPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/lgPlatform.png
--------------------------------------------------------------------------------
/src/img/level2/mdPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/mdPlatform.png
--------------------------------------------------------------------------------
/src/img/level2/mountain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/mountain.png
--------------------------------------------------------------------------------
/src/img/level2/mountains.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/mountains.png
--------------------------------------------------------------------------------
/src/img/level2/sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/level2/sun.png
--------------------------------------------------------------------------------
/src/img/lgPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/lgPlatform.png
--------------------------------------------------------------------------------
/src/img/mario/spriteFireFlowerShootLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/mario/spriteFireFlowerShootLeft.png
--------------------------------------------------------------------------------
/src/img/mario/spriteFireFlowerShootRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/mario/spriteFireFlowerShootRight.png
--------------------------------------------------------------------------------
/src/img/mdPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/mdPlatform.png
--------------------------------------------------------------------------------
/src/img/platform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/platform.png
--------------------------------------------------------------------------------
/src/img/platformSmallTall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/platformSmallTall.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlower.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerJumpLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerJumpLeft.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerJumpRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerJumpRight.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerRunLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerRunLeft.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerRunRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerRunRight.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerStandLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerStandLeft.png
--------------------------------------------------------------------------------
/src/img/spriteFireFlowerStandRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteFireFlowerStandRight.png
--------------------------------------------------------------------------------
/src/img/spriteGoomba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteGoomba.png
--------------------------------------------------------------------------------
/src/img/spriteMarioJumpLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioJumpLeft.png
--------------------------------------------------------------------------------
/src/img/spriteMarioJumpRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioJumpRight.png
--------------------------------------------------------------------------------
/src/img/spriteMarioRunLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioRunLeft.png
--------------------------------------------------------------------------------
/src/img/spriteMarioRunRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioRunRight.png
--------------------------------------------------------------------------------
/src/img/spriteMarioStandLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioStandLeft.png
--------------------------------------------------------------------------------
/src/img/spriteMarioStandRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteMarioStandRight.png
--------------------------------------------------------------------------------
/src/img/spriteRunLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteRunLeft.png
--------------------------------------------------------------------------------
/src/img/spriteRunRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteRunRight.png
--------------------------------------------------------------------------------
/src/img/spriteStandLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteStandLeft.png
--------------------------------------------------------------------------------
/src/img/spriteStandRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/spriteStandRight.png
--------------------------------------------------------------------------------
/src/img/tPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/tPlatform.png
--------------------------------------------------------------------------------
/src/img/xtPlatform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscourses/mario-game/cf452b2a295a5f8b226e7847c60dc1179b68a31b/src/img/xtPlatform.png
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Canvas Boilerplate | Chris Courses
6 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/js/audio.js:
--------------------------------------------------------------------------------
1 | import { Howl } from 'howler'
2 | import audioCompleteLevel from '../audio/audioCompleteLevel.mp3'
3 | import audioDescend from '../audio/audioDescend.mp3'
4 | import audioDie from '../audio/audioDie.mp3'
5 | import audioFireFlowerShot from '../audio/audioFireFlowerShot.mp3'
6 | import audioFireworkBurst from '../audio/audioFireworkBurst.mp3'
7 | import audioFireworkWhistle from '../audio/audioFireworkWhistle.mp3'
8 | import audioGameOver from '../audio/audioGameOver.mp3'
9 | import audioJump from '../audio/audioJump.mp3'
10 | import audioLosePowerUp from '../audio/audioLosePowerUp.mp3'
11 | import audioMusicLevel1 from '../audio/audioMusicLevel1.mp3'
12 | import audioObtainPowerUp from '../audio/audioWinLevel.mp3'
13 | import audioGoombaSquash from '../audio/audioGoombaSquash.mp3'
14 |
15 | export const audio = {
16 | completeLevel: new Howl({
17 | src: [audioCompleteLevel],
18 | volume: 0.1
19 | }),
20 | descend: new Howl({
21 | src: [audioDescend],
22 | volume: 0.1
23 | }),
24 | die: new Howl({
25 | src: [audioDie],
26 | volume: 0.1
27 | }),
28 | fireFlowerShot: new Howl({
29 | src: [audioFireFlowerShot],
30 | volume: 0.1
31 | }),
32 | fireworkBurst: new Howl({
33 | src: [audioFireworkBurst],
34 | volume: 0.1
35 | }),
36 |
37 | fireworkWhistle: new Howl({
38 | src: [audioFireworkWhistle],
39 | volume: 0.1
40 | }),
41 | gameOver: new Howl({
42 | src: [audioGameOver],
43 | volume: 0.1
44 | }),
45 | jump: new Howl({
46 | src: [audioJump],
47 | volume: 0.1
48 | }),
49 | losePowerUp: new Howl({
50 | src: [audioLosePowerUp],
51 | volume: 0.1
52 | }),
53 | musicLevel1: new Howl({
54 | src: [audioMusicLevel1],
55 | volume: 0.1,
56 | loop: true,
57 | autoplay: true
58 | }),
59 | obtainPowerUp: new Howl({
60 | src: [audioObtainPowerUp],
61 | volume: 0.1
62 | }),
63 | goombaSquash: new Howl({
64 | src: [audioGoombaSquash],
65 | volume: 0.1
66 | })
67 | }
68 |
--------------------------------------------------------------------------------
/src/js/canvas.js:
--------------------------------------------------------------------------------
1 | import gsap from 'gsap'
2 | import {
3 | createImage,
4 | createImageAsync,
5 | isOnTopOfPlatform,
6 | collisionTop,
7 | isOnTopOfPlatformCircle,
8 | hitBottomOfPlatform,
9 | hitSideOfPlatform,
10 | objectsTouch
11 | } from './utils.js'
12 |
13 | import platform from '../img/platform.png'
14 | import hills from '../img/hills.png'
15 | import background from '../img/background.png'
16 | import platformSmallTall from '../img/platformSmallTall.png'
17 | import block from '../img/block.png'
18 | import blockTri from '../img/blockTri.png'
19 | import mdPlatform from '../img/mdPlatform.png'
20 | import lgPlatform from '../img/lgPlatform.png'
21 | import tPlatform from '../img/tPlatform.png'
22 | import xtPlatform from '../img/xtPlatform.png'
23 | import flagPoleSprite from '../img/flagPole.png'
24 |
25 | import spriteRunLeft from '../img/spriteRunLeft.png'
26 | import spriteRunRight from '../img/spriteRunRight.png'
27 | import spriteStandLeft from '../img/spriteStandLeft.png'
28 | import spriteStandRight from '../img/spriteStandRight.png'
29 |
30 | import spriteMarioRunLeft from '../img/spriteMarioRunLeft.png'
31 | import spriteMarioRunRight from '../img/spriteMarioRunRight.png'
32 | import spriteMarioStandLeft from '../img/spriteMarioStandLeft.png'
33 | import spriteMarioStandRight from '../img/spriteMarioStandRight.png'
34 | import spriteMarioJumpRight from '../img/spriteMarioJumpRight.png'
35 | import spriteMarioJumpLeft from '../img/spriteMarioJumpLeft.png'
36 |
37 | import spriteFireFlowerRunRight from '../img/spriteFireFlowerRunRight.png'
38 | import spriteFireFlowerRunLeft from '../img/spriteFireFlowerRunLeft.png'
39 | import spriteFireFlowerStandRight from '../img/spriteFireFlowerStandRight.png'
40 | import spriteFireFlowerStandLeft from '../img/spriteFireFlowerStandLeft.png'
41 | import spriteFireFlowerJumpRight from '../img/spriteFireFlowerJumpRight.png'
42 | import spriteFireFlowerJumpLeft from '../img/spriteFireFlowerJumpLeft.png'
43 |
44 | import spriteFireFlower from '../img/spriteFireFlower.png'
45 |
46 | import spriteGoomba from '../img/spriteGoomba.png'
47 | import { audio } from './audio.js'
48 | import { images } from './images.js'
49 |
50 | const canvas = document.querySelector('canvas')
51 | const c = canvas.getContext('2d')
52 |
53 | canvas.width = 1024
54 | canvas.height = 576
55 |
56 | let gravity = 1.5
57 |
58 | class Player {
59 | constructor() {
60 | this.shooting = false
61 | this.speed = 10
62 | this.position = {
63 | x: 100,
64 | y: 100
65 | }
66 | this.velocity = {
67 | x: 0,
68 | y: 0
69 | }
70 |
71 | this.scale = 0.3
72 | this.width = 398 * this.scale
73 | this.height = 353 * this.scale
74 |
75 | this.image = createImage(spriteStandRight)
76 | this.frames = 0
77 | this.sprites = {
78 | stand: {
79 | right: createImage(spriteMarioStandRight),
80 | left: createImage(spriteMarioStandLeft),
81 | fireFlower: {
82 | right: createImage(spriteFireFlowerStandRight),
83 | left: createImage(spriteFireFlowerStandLeft)
84 | }
85 | },
86 | run: {
87 | right: createImage(spriteMarioRunRight),
88 | left: createImage(spriteMarioRunLeft),
89 | fireFlower: {
90 | right: createImage(spriteFireFlowerRunRight),
91 | left: createImage(spriteFireFlowerRunLeft)
92 | }
93 | },
94 | jump: {
95 | right: createImage(spriteMarioJumpRight),
96 | left: createImage(spriteMarioJumpLeft),
97 | fireFlower: {
98 | right: createImage(spriteFireFlowerJumpRight),
99 | left: createImage(spriteFireFlowerJumpLeft)
100 | }
101 | },
102 | shoot: {
103 | fireFlower: {
104 | right: createImage(images.mario.shoot.fireFlower.right),
105 | left: createImage(images.mario.shoot.fireFlower.left)
106 | }
107 | }
108 | }
109 |
110 | this.currentSprite = this.sprites.stand.right
111 | this.currentCropWidth = 398
112 | this.powerUps = {
113 | fireFlower: false
114 | }
115 | this.invincible = false
116 | this.opacity = 1
117 | }
118 |
119 | draw() {
120 | c.save()
121 | c.globalAlpha = this.opacity
122 | c.fillStyle = 'rgba(255, 0, 0, .2)'
123 | c.fillRect(this.position.x, this.position.y, this.width, this.height)
124 | c.drawImage(
125 | this.currentSprite,
126 | this.currentCropWidth * this.frames,
127 | 0,
128 | this.currentCropWidth,
129 | 353,
130 | this.position.x,
131 | this.position.y,
132 | this.width,
133 | this.height
134 | )
135 | c.restore()
136 | }
137 |
138 | update() {
139 | this.frames++
140 | const { currentSprite, sprites } = this
141 |
142 | if (
143 | this.frames > 58 &&
144 | (currentSprite === sprites.stand.right ||
145 | currentSprite === sprites.stand.left ||
146 | currentSprite === sprites.stand.fireFlower.left ||
147 | currentSprite === sprites.stand.fireFlower.right)
148 | )
149 | this.frames = 0
150 | else if (
151 | this.frames > 28 &&
152 | (currentSprite === sprites.run.right ||
153 | currentSprite === sprites.run.left ||
154 | currentSprite === sprites.run.fireFlower.right ||
155 | currentSprite === sprites.run.fireFlower.left)
156 | )
157 | this.frames = 0
158 | else if (
159 | currentSprite === sprites.jump.right ||
160 | currentSprite === sprites.jump.left ||
161 | currentSprite === sprites.jump.fireFlower.right ||
162 | currentSprite === sprites.jump.fireFlower.left ||
163 | currentSprite === sprites.shoot.fireFlower.left ||
164 | currentSprite === sprites.shoot.fireFlower.right
165 | )
166 | this.frames = 0
167 |
168 | this.draw()
169 | this.position.x += this.velocity.x
170 | this.position.y += this.velocity.y
171 |
172 | if (this.position.y + this.height + this.velocity.y <= canvas.height)
173 | this.velocity.y += gravity
174 |
175 | if (this.invincible) {
176 | if (this.opacity === 1) this.opacity = 0
177 | else this.opacity = 1
178 | } else this.opacity = 1
179 | }
180 | }
181 |
182 | class Platform {
183 | constructor({ x, y, image, block, text }) {
184 | this.position = {
185 | x,
186 | y
187 | }
188 |
189 | this.velocity = {
190 | x: 0
191 | }
192 |
193 | this.image = image
194 | this.width = image.width
195 | this.height = image.height
196 | this.block = block
197 | this.text = text
198 | }
199 |
200 | draw() {
201 | c.drawImage(this.image, this.position.x, this.position.y)
202 |
203 | if (this.text) {
204 | c.font = '20px Arial'
205 | c.fillStyle = 'red'
206 | c.fillText(this.text, this.position.x, this.position.y)
207 | }
208 | }
209 |
210 | update() {
211 | this.draw()
212 | this.position.x += this.velocity.x
213 | }
214 | }
215 |
216 | class GenericObject {
217 | constructor({ x, y, image }) {
218 | this.position = {
219 | x,
220 | y
221 | }
222 |
223 | this.velocity = {
224 | x: 0
225 | }
226 |
227 | this.image = image
228 | this.width = image.width
229 | this.height = image.height
230 | }
231 |
232 | draw() {
233 | c.drawImage(this.image, this.position.x, this.position.y)
234 | }
235 |
236 | update() {
237 | this.draw()
238 | this.position.x += this.velocity.x
239 | }
240 | }
241 |
242 | class Goomba {
243 | constructor({
244 | position,
245 | velocity,
246 | distance = {
247 | limit: 50,
248 | traveled: 0
249 | }
250 | }) {
251 | this.position = {
252 | x: position.x,
253 | y: position.y
254 | }
255 |
256 | this.velocity = {
257 | x: velocity.x,
258 | y: velocity.y
259 | }
260 |
261 | this.width = 43.33
262 | this.height = 50
263 |
264 | this.image = createImage(spriteGoomba)
265 | this.frames = 0
266 |
267 | this.distance = distance
268 | }
269 |
270 | draw() {
271 | // c.fillStyle = 'red'
272 | // c.fillRect(this.position.x, this.position.y, this.width, this.height)
273 |
274 | c.drawImage(
275 | this.image,
276 | 130 * this.frames,
277 | 0,
278 | 130,
279 | 150,
280 | this.position.x,
281 | this.position.y,
282 | this.width,
283 | this.height
284 | )
285 | }
286 |
287 | update() {
288 | this.frames++
289 | if (this.frames >= 58) this.frames = 0
290 | this.draw()
291 | this.position.x += this.velocity.x
292 | this.position.y += this.velocity.y
293 |
294 | if (this.position.y + this.height + this.velocity.y <= canvas.height)
295 | this.velocity.y += gravity
296 |
297 | // walk the goomba back and forth
298 | this.distance.traveled += Math.abs(this.velocity.x)
299 |
300 | if (this.distance.traveled > this.distance.limit) {
301 | this.distance.traveled = 0
302 | this.velocity.x = -this.velocity.x
303 | }
304 | }
305 | }
306 |
307 | class FireFlower {
308 | constructor({ position, velocity }) {
309 | this.position = {
310 | x: position.x,
311 | y: position.y
312 | }
313 |
314 | this.velocity = {
315 | x: velocity.x,
316 | y: velocity.y
317 | }
318 |
319 | this.width = 56
320 | this.height = 60
321 |
322 | this.image = createImage(spriteFireFlower)
323 | this.frames = 0
324 | }
325 |
326 | draw() {
327 | // c.fillStyle = 'red'
328 | // c.fillRect(this.position.x, this.position.y, this.width, this.height)
329 |
330 | c.drawImage(
331 | this.image,
332 | 56 * this.frames,
333 | 0,
334 | 56,
335 | 60,
336 | this.position.x,
337 | this.position.y,
338 | this.width,
339 | this.height
340 | )
341 | }
342 |
343 | update() {
344 | this.frames++
345 | if (this.frames >= 75) this.frames = 0
346 | this.draw()
347 | this.position.x += this.velocity.x
348 | this.position.y += this.velocity.y
349 |
350 | if (this.position.y + this.height + this.velocity.y <= canvas.height)
351 | this.velocity.y += gravity
352 | }
353 | }
354 |
355 | class Particle {
356 | constructor({
357 | position,
358 | velocity,
359 | radius,
360 | color = '#654428',
361 | fireball = false,
362 | fades = false
363 | }) {
364 | this.position = {
365 | x: position.x,
366 | y: position.y
367 | }
368 |
369 | this.velocity = {
370 | x: velocity.x,
371 | y: velocity.y
372 | }
373 |
374 | this.radius = radius
375 | this.ttl = 300
376 | this.color = color
377 | this.fireball = fireball
378 | this.opacity = 1
379 | this.fades = fades
380 | }
381 |
382 | draw() {
383 | c.save()
384 | c.globalAlpha = this.opacity
385 | c.beginPath()
386 | c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2, false)
387 | c.fillStyle = this.color
388 | c.fill()
389 | c.closePath()
390 | c.restore()
391 | }
392 |
393 | update() {
394 | this.ttl--
395 | this.draw()
396 | this.position.x += this.velocity.x
397 | this.position.y += this.velocity.y
398 |
399 | if (this.position.y + this.radius + this.velocity.y <= canvas.height)
400 | this.velocity.y += gravity * 0.4
401 |
402 | if (this.fades && this.opacity > 0) {
403 | this.opacity -= 0.01
404 | }
405 |
406 | if (this.opacity < 0) this.opacity = 0
407 | }
408 | }
409 |
410 | let platformImage
411 | let platformSmallTallImage
412 | let blockTriImage
413 | let lgPlatformImage
414 | let tPlatformImage
415 | let xtPlatformImage
416 | let blockImage
417 |
418 | let player = new Player()
419 | let platforms = []
420 | let genericObjects = []
421 | let goombas = []
422 | let particles = []
423 | let fireFlowers = []
424 |
425 | let lastKey
426 | let keys
427 |
428 | let scrollOffset
429 | let flagPole
430 | let flagPoleImage
431 | let game
432 | let currentLevel = 1
433 |
434 | function selectLevel(currentLevel) {
435 | if (!audio.musicLevel1.playing()) audio.musicLevel1.play()
436 | switch (currentLevel) {
437 | case 1:
438 | init()
439 | break
440 | case 2:
441 | initLevel2()
442 | break
443 | }
444 | }
445 |
446 | async function init() {
447 | player = new Player()
448 | keys = {
449 | right: {
450 | pressed: false
451 | },
452 | left: {
453 | pressed: false
454 | }
455 | }
456 | scrollOffset = 0
457 |
458 | game = {
459 | disableUserInput: false
460 | }
461 |
462 | platformImage = await createImageAsync(platform)
463 | platformSmallTallImage = await createImageAsync(platformSmallTall)
464 | blockTriImage = await createImageAsync(blockTri)
465 | blockImage = await createImageAsync(block)
466 | lgPlatformImage = await createImageAsync(lgPlatform)
467 | tPlatformImage = await createImageAsync(tPlatform)
468 | xtPlatformImage = await createImageAsync(xtPlatform)
469 | flagPoleImage = await createImageAsync(flagPoleSprite)
470 |
471 | flagPole = new GenericObject({
472 | x: 6968 + 600,
473 | // x: 500,
474 | y: canvas.height - lgPlatformImage.height - flagPoleImage.height,
475 | image: flagPoleImage
476 | })
477 |
478 | fireFlowers = [
479 | new FireFlower({
480 | position: {
481 | x: 400,
482 | y: 100
483 | },
484 | velocity: {
485 | x: 0,
486 | y: 0
487 | }
488 | })
489 | ]
490 |
491 | player = new Player()
492 |
493 | const goombaWidth = 43.33
494 | goombas = [
495 | new Goomba({
496 | position: {
497 | x: 908 + lgPlatformImage.width - goombaWidth,
498 | y: 100
499 | },
500 | velocity: {
501 | x: -0.3,
502 | y: 0
503 | },
504 | distance: {
505 | limit: 400,
506 | traveled: 0
507 | }
508 | }),
509 | new Goomba({
510 | position: {
511 | x: 3249 + lgPlatformImage.width - goombaWidth,
512 | y: 100
513 | },
514 | velocity: {
515 | x: -0.3,
516 | y: 0
517 | },
518 | distance: {
519 | limit: 400,
520 | traveled: 0
521 | }
522 | }),
523 | new Goomba({
524 | position: {
525 | x: 3249 + lgPlatformImage.width - goombaWidth - goombaWidth,
526 | y: 100
527 | },
528 | velocity: {
529 | x: -0.3,
530 | y: 0
531 | },
532 | distance: {
533 | limit: 400,
534 | traveled: 0
535 | }
536 | }),
537 | new Goomba({
538 | position: {
539 | x:
540 | 3249 +
541 | lgPlatformImage.width -
542 | goombaWidth -
543 | goombaWidth -
544 | goombaWidth,
545 | y: 100
546 | },
547 | velocity: {
548 | x: -0.3,
549 | y: 0
550 | },
551 | distance: {
552 | limit: 400,
553 | traveled: 0
554 | }
555 | }),
556 | new Goomba({
557 | position: {
558 | x:
559 | 3249 +
560 | lgPlatformImage.width -
561 | goombaWidth -
562 | goombaWidth -
563 | goombaWidth -
564 | goombaWidth,
565 | y: 100
566 | },
567 | velocity: {
568 | x: -0.3,
569 | y: 0
570 | },
571 | distance: {
572 | limit: 400,
573 | traveled: 0
574 | }
575 | }),
576 | new Goomba({
577 | position: {
578 | x: 5135 + xtPlatformImage.width / 2 + goombaWidth,
579 | y: 100
580 | },
581 | velocity: {
582 | x: -0.3,
583 | y: 0
584 | },
585 | distance: {
586 | limit: 100,
587 | traveled: 0
588 | }
589 | }),
590 | new Goomba({
591 | position: {
592 | x: 6968,
593 | y: 0
594 | },
595 | velocity: {
596 | x: -0.3,
597 | y: 0
598 | },
599 | distance: {
600 | limit: 100,
601 | traveled: 0
602 | }
603 | })
604 | ]
605 | particles = []
606 | platforms = [
607 | new Platform({
608 | x: 908 + 100,
609 | y: 300,
610 | image: blockTriImage,
611 | block: true
612 | }),
613 | new Platform({
614 | x: 908 + 100 + blockImage.width,
615 | y: 100,
616 | image: blockImage,
617 | block: true
618 | }),
619 | new Platform({
620 | x: 1991 + lgPlatformImage.width - tPlatformImage.width,
621 | y: canvas.height - lgPlatformImage.height - tPlatformImage.height,
622 | image: tPlatformImage,
623 | block: false
624 | }),
625 | new Platform({
626 | x: 1991 + lgPlatformImage.width - tPlatformImage.width - 100,
627 | y:
628 | canvas.height -
629 | lgPlatformImage.height -
630 | tPlatformImage.height +
631 | blockImage.height,
632 | image: blockImage,
633 | block: true
634 | }),
635 | new Platform({
636 | x: 5712 + xtPlatformImage.width + 175,
637 | y: canvas.height - xtPlatformImage.height,
638 | image: blockImage,
639 | block: true,
640 | text: 5712 + xtPlatformImage.width + 175
641 | }),
642 | new Platform({
643 | x: 6116 + 175,
644 | y: canvas.height - xtPlatformImage.height,
645 | image: blockImage,
646 | block: true
647 | }),
648 | new Platform({
649 | x: 6116 + 175 * 2,
650 | y: canvas.height - xtPlatformImage.height,
651 | image: blockImage,
652 | block: true
653 | }),
654 | new Platform({
655 | x: 6116 + 175 * 3,
656 | y: canvas.height - xtPlatformImage.height - 100,
657 | image: blockImage,
658 | block: true
659 | }),
660 | new Platform({
661 | x: 6116 + 175 * 4,
662 | y: canvas.height - xtPlatformImage.height - 200,
663 | image: blockTriImage,
664 | block: true
665 | }),
666 | new Platform({
667 | x: 6116 + 175 * 4 + blockTriImage.width,
668 | y: canvas.height - xtPlatformImage.height - 200,
669 | image: blockTriImage,
670 | block: true,
671 | text: 6116 + 175 * 4 + blockTriImage.width
672 | }),
673 | new Platform({
674 | x: 6968 + 300,
675 | y: canvas.height - lgPlatformImage.height,
676 | image: lgPlatformImage,
677 | block: true,
678 | text: 6968 + 300
679 | })
680 | ]
681 | genericObjects = [
682 | new GenericObject({
683 | x: -1,
684 | y: -1,
685 | image: createImage(background)
686 | }),
687 | new GenericObject({
688 | x: -1,
689 | y: -1,
690 | image: createImage(hills)
691 | })
692 | ]
693 |
694 | scrollOffset = 0
695 |
696 | const platformsMap = [
697 | 'lg',
698 | 'lg',
699 | 'gap',
700 | 'lg',
701 | 'gap',
702 | 'gap',
703 | 'lg',
704 | 'gap',
705 | 't',
706 | 'gap',
707 | 'xt',
708 | 'gap',
709 | 'xt',
710 | 'gap',
711 | 'gap',
712 | 'xt'
713 | ]
714 |
715 | let platformDistance = 0
716 |
717 | platformsMap.forEach((symbol) => {
718 | switch (symbol) {
719 | case 'lg':
720 | platforms.push(
721 | new Platform({
722 | x: platformDistance,
723 | y: canvas.height - lgPlatformImage.height,
724 | image: lgPlatformImage,
725 | block: true,
726 | text: platformDistance
727 | })
728 | )
729 |
730 | platformDistance += lgPlatformImage.width - 2
731 |
732 | break
733 |
734 | case 'gap':
735 | platformDistance += 175
736 |
737 | break
738 |
739 | case 't':
740 | platforms.push(
741 | new Platform({
742 | x: platformDistance,
743 | y: canvas.height - tPlatformImage.height,
744 | image: tPlatformImage,
745 | block: true
746 | })
747 | )
748 |
749 | platformDistance += tPlatformImage.width - 2
750 |
751 | break
752 |
753 | case 'xt':
754 | platforms.push(
755 | new Platform({
756 | x: platformDistance,
757 | y: canvas.height - xtPlatformImage.height,
758 | image: xtPlatformImage,
759 | block: true,
760 | text: platformDistance
761 | })
762 | )
763 |
764 | platformDistance += xtPlatformImage.width - 2
765 |
766 | break
767 | }
768 | })
769 | }
770 |
771 | async function initLevel2() {
772 | player = new Player()
773 | keys = {
774 | right: {
775 | pressed: false
776 | },
777 | left: {
778 | pressed: false
779 | }
780 | }
781 | scrollOffset = 0
782 |
783 | game = {
784 | disableUserInput: false
785 | }
786 |
787 | blockTriImage = await createImageAsync(blockTri)
788 | blockImage = await createImageAsync(block)
789 | lgPlatformImage = await createImageAsync(images.levels[2].lgPlatform)
790 | tPlatformImage = await createImageAsync(tPlatform)
791 | xtPlatformImage = await createImageAsync(xtPlatform)
792 | flagPoleImage = await createImageAsync(flagPoleSprite)
793 | const mountains = await createImageAsync(images.levels[2].mountains)
794 | const mdPlatformImage = await createImageAsync(images.levels[2].mdPlatform)
795 |
796 | flagPole = new GenericObject({
797 | x: 7680,
798 | // x: 500,
799 | y: canvas.height - lgPlatformImage.height - flagPoleImage.height,
800 | image: flagPoleImage
801 | })
802 |
803 | fireFlowers = [
804 | new FireFlower({
805 | position: {
806 | x: 4734 - 28,
807 | y: 100
808 | },
809 | velocity: {
810 | x: 0,
811 | y: 0
812 | }
813 | })
814 | ]
815 |
816 | player = new Player()
817 |
818 | const goombaWidth = 43.33
819 | goombas = [
820 | new Goomba({
821 | // single block goomba
822 | position: {
823 | x: 903 + mdPlatformImage.width - goombaWidth,
824 | y: 100
825 | },
826 | velocity: {
827 | x: -2,
828 | y: 0
829 | },
830 | distance: {
831 | limit: 700,
832 | traveled: 0
833 | }
834 | }),
835 | new Goomba({
836 | // single block goomba
837 | position: {
838 | x:
839 | 1878 +
840 | lgPlatformImage.width +
841 | 155 +
842 | 200 +
843 | 200 +
844 | 200 +
845 | blockImage.width / 2 -
846 | goombaWidth / 2,
847 | y: 100
848 | },
849 | velocity: {
850 | x: 0,
851 | y: 0
852 | },
853 | distance: {
854 | limit: 0,
855 | traveled: 0
856 | }
857 | }),
858 | new Goomba({
859 | position: {
860 | x: 3831 + lgPlatformImage.width - goombaWidth,
861 | y: 100
862 | },
863 | velocity: {
864 | x: -1,
865 | y: 0
866 | },
867 | distance: {
868 | limit: lgPlatformImage.width - goombaWidth,
869 | traveled: 0
870 | }
871 | }),
872 |
873 | new Goomba({
874 | position: {
875 | x: 4734,
876 | y: 100
877 | },
878 | velocity: {
879 | x: 1,
880 | y: 0
881 | },
882 | distance: {
883 | limit: lgPlatformImage.width - goombaWidth,
884 | traveled: 0
885 | }
886 | })
887 | ]
888 | particles = []
889 | platforms = [
890 | new Platform({
891 | x: 903 + mdPlatformImage.width + 115,
892 | y: 300,
893 | image: blockTriImage,
894 | block: true
895 | }),
896 | new Platform({
897 | x: 903 + mdPlatformImage.width + 115 + blockTriImage.width,
898 | y: 300,
899 | image: blockTriImage,
900 | block: true
901 | }),
902 | new Platform({
903 | x: 1878 + lgPlatformImage.width + 175,
904 | y: 360,
905 | image: blockImage,
906 | block: true
907 | }),
908 | new Platform({
909 | x: 1878 + lgPlatformImage.width + 155 + 200,
910 | y: 300,
911 | image: blockImage,
912 | block: true
913 | }),
914 | new Platform({
915 | x: 1878 + lgPlatformImage.width + 155 + 200 + 200,
916 | y: 330,
917 | image: blockImage,
918 | block: true
919 | }),
920 | new Platform({
921 | x: 1878 + lgPlatformImage.width + 155 + 200 + 200 + 200,
922 | y: 240,
923 | image: blockImage,
924 | block: true
925 | }),
926 | new Platform({
927 | x: 4734 - mdPlatformImage.width / 2,
928 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height,
929 | image: mdPlatformImage
930 | }),
931 | new Platform({
932 | x: 5987,
933 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height,
934 | image: mdPlatformImage
935 | }),
936 | new Platform({
937 | x: 5987,
938 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height * 2,
939 | image: mdPlatformImage
940 | }),
941 | new Platform({
942 | x: 6787,
943 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height,
944 | image: mdPlatformImage
945 | }),
946 | new Platform({
947 | x: 6787,
948 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height * 2,
949 | image: mdPlatformImage
950 | }),
951 | new Platform({
952 | x: 6787,
953 | y: canvas.height - lgPlatformImage.height - mdPlatformImage.height * 3,
954 | image: mdPlatformImage
955 | })
956 | ]
957 | genericObjects = [
958 | new GenericObject({
959 | x: -1,
960 | y: -1,
961 | image: createImage(images.levels[2].background)
962 | }),
963 | new GenericObject({
964 | x: -1,
965 | y: canvas.height - mountains.height,
966 | image: mountains
967 | })
968 | ]
969 |
970 | scrollOffset = 0
971 |
972 | const platformsMap = [
973 | 'lg',
974 | 'md',
975 | 'gap',
976 | 'gap',
977 | 'gap',
978 | 'lg',
979 | 'gap',
980 | 'gap',
981 | 'gap',
982 | 'gap',
983 | 'gap',
984 | 'gap',
985 | 'lg',
986 | 'lg',
987 | 'gap',
988 | 'gap',
989 | 'md',
990 | 'gap',
991 | 'gap',
992 | 'md',
993 | 'gap',
994 | 'gap',
995 | 'lg'
996 | ]
997 |
998 | let platformDistance = 0
999 |
1000 | platformsMap.forEach((symbol) => {
1001 | switch (symbol) {
1002 | case 'md':
1003 | platforms.push(
1004 | new Platform({
1005 | x: platformDistance,
1006 | y: canvas.height - mdPlatformImage.height,
1007 | image: mdPlatformImage,
1008 | block: true,
1009 | text: platformDistance
1010 | })
1011 | )
1012 |
1013 | platformDistance += mdPlatformImage.width - 3
1014 |
1015 | break
1016 | case 'lg':
1017 | platforms.push(
1018 | new Platform({
1019 | x: platformDistance - 2,
1020 | y: canvas.height - lgPlatformImage.height,
1021 | image: lgPlatformImage,
1022 | block: true,
1023 | text: platformDistance
1024 | })
1025 | )
1026 |
1027 | platformDistance += lgPlatformImage.width - 3
1028 |
1029 | break
1030 |
1031 | case 'gap':
1032 | platformDistance += 175
1033 |
1034 | break
1035 |
1036 | case 't':
1037 | platforms.push(
1038 | new Platform({
1039 | x: platformDistance,
1040 | y: canvas.height - tPlatformImage.height,
1041 | image: tPlatformImage,
1042 | block: true
1043 | })
1044 | )
1045 |
1046 | platformDistance += tPlatformImage.width - 2
1047 |
1048 | break
1049 |
1050 | case 'xt':
1051 | platforms.push(
1052 | new Platform({
1053 | x: platformDistance,
1054 | y: canvas.height - xtPlatformImage.height,
1055 | image: xtPlatformImage,
1056 | block: true,
1057 | text: platformDistance
1058 | })
1059 | )
1060 |
1061 | platformDistance += xtPlatformImage.width - 2
1062 |
1063 | break
1064 | }
1065 | })
1066 | }
1067 |
1068 | function animate() {
1069 | requestAnimationFrame(animate)
1070 | c.fillStyle = 'white'
1071 | c.fillRect(0, 0, canvas.width, canvas.height)
1072 |
1073 | genericObjects.forEach((genericObject) => {
1074 | genericObject.update()
1075 | genericObject.velocity.x = 0
1076 | })
1077 |
1078 | particles.forEach((particle, i) => {
1079 | particle.update()
1080 |
1081 | if (
1082 | particle.fireball &&
1083 | (particle.position.x - particle.radius >= canvas.width ||
1084 | particle.position.x + particle.radius <= 0)
1085 | )
1086 | setTimeout(() => {
1087 | particles.splice(i, 1)
1088 | }, 0)
1089 | })
1090 |
1091 | platforms.forEach((platform) => {
1092 | platform.update()
1093 | platform.velocity.x = 0
1094 | })
1095 |
1096 | if (flagPole) {
1097 | flagPole.update()
1098 | flagPole.velocity.x = 0
1099 |
1100 | // mario touches flagpole
1101 | // win condition
1102 | // complete level
1103 | if (
1104 | !game.disableUserInput &&
1105 | objectsTouch({
1106 | object1: player,
1107 | object2: flagPole
1108 | })
1109 | ) {
1110 | audio.completeLevel.play()
1111 | audio.musicLevel1.stop()
1112 | game.disableUserInput = true
1113 | player.velocity.x = 0
1114 | player.velocity.y = 0
1115 | gravity = 0
1116 |
1117 | player.currentSprite = player.sprites.stand.right
1118 |
1119 | if (player.powerUps.fireFlower)
1120 | player.currentSprite = player.sprites.stand.fireFlower.right
1121 |
1122 | // flagpole slide
1123 | setTimeout(() => {
1124 | audio.descend.play()
1125 | }, 200)
1126 | gsap.to(player.position, {
1127 | y: canvas.height - lgPlatformImage.height - player.height,
1128 | duration: 1,
1129 | onComplete() {
1130 | player.currentSprite = player.sprites.run.right
1131 |
1132 | if (player.powerUps.fireFlower)
1133 | player.currentSprite = player.sprites.run.fireFlower.right
1134 | }
1135 | })
1136 |
1137 | gsap.to(player.position, {
1138 | delay: 1,
1139 | x: canvas.width,
1140 | duration: 2,
1141 | ease: 'power1.in'
1142 | })
1143 |
1144 | // fireworks
1145 | const particleCount = 300
1146 | const radians = (Math.PI * 2) / particleCount
1147 | const power = 8
1148 | let increment = 1
1149 |
1150 | const intervalId = setInterval(() => {
1151 | for (let i = 0; i < particleCount; i++) {
1152 | particles.push(
1153 | new Particle({
1154 | position: {
1155 | x: (canvas.width / 4) * increment,
1156 | y: canvas.height / 2
1157 | },
1158 | velocity: {
1159 | x: Math.cos(radians * i) * power * Math.random(),
1160 | y: Math.sin(radians * i) * power * Math.random()
1161 | },
1162 | radius: 3 * Math.random(),
1163 | color: `hsl(${Math.random() * 200}, 50%, 50%)`,
1164 | fades: true
1165 | })
1166 | )
1167 | }
1168 |
1169 | audio.fireworkBurst.play()
1170 | audio.fireworkWhistle.play()
1171 |
1172 | if (increment === 3) clearInterval(intervalId)
1173 |
1174 | increment++
1175 | }, 1000)
1176 |
1177 | // switch to the next level
1178 | setTimeout(() => {
1179 | currentLevel++
1180 | gravity = 1.5
1181 | selectLevel(currentLevel)
1182 | }, 8000)
1183 | }
1184 | }
1185 |
1186 | // mario obtains powerup
1187 | fireFlowers.forEach((fireFlower, i) => {
1188 | if (
1189 | objectsTouch({
1190 | object1: player,
1191 | object2: fireFlower
1192 | })
1193 | ) {
1194 | audio.obtainPowerUp.play()
1195 | player.powerUps.fireFlower = true
1196 | setTimeout(() => {
1197 | fireFlowers.splice(i, 1)
1198 | }, 0)
1199 | } else fireFlower.update()
1200 | })
1201 |
1202 | goombas.forEach((goomba, index) => {
1203 | goomba.update()
1204 |
1205 | // remove goomba on fireball hit
1206 | particles.forEach((particle, particleIndex) => {
1207 | if (
1208 | particle.fireball &&
1209 | particle.position.x + particle.radius >= goomba.position.x &&
1210 | particle.position.y + particle.radius >= goomba.position.y &&
1211 | particle.position.x - particle.radius <=
1212 | goomba.position.x + goomba.width &&
1213 | particle.position.y - particle.radius <=
1214 | goomba.position.y + goomba.height
1215 | ) {
1216 | for (let i = 0; i < 50; i++) {
1217 | particles.push(
1218 | new Particle({
1219 | position: {
1220 | x: goomba.position.x + goomba.width / 2,
1221 | y: goomba.position.y + goomba.height / 2
1222 | },
1223 | velocity: {
1224 | x: (Math.random() - 0.5) * 7,
1225 | y: (Math.random() - 0.5) * 15
1226 | },
1227 | radius: Math.random() * 3
1228 | })
1229 | )
1230 | }
1231 | setTimeout(() => {
1232 | goombas.splice(index, 1)
1233 | particles.splice(particleIndex, 1)
1234 | }, 0)
1235 | }
1236 | })
1237 |
1238 | // goomba stomp squish / squash
1239 | if (
1240 | collisionTop({
1241 | object1: player,
1242 | object2: goomba
1243 | })
1244 | ) {
1245 | audio.goombaSquash.play()
1246 |
1247 | for (let i = 0; i < 50; i++) {
1248 | particles.push(
1249 | new Particle({
1250 | position: {
1251 | x: goomba.position.x + goomba.width / 2,
1252 | y: goomba.position.y + goomba.height / 2
1253 | },
1254 | velocity: {
1255 | x: (Math.random() - 0.5) * 7,
1256 | y: (Math.random() - 0.5) * 15
1257 | },
1258 | radius: Math.random() * 3
1259 | })
1260 | )
1261 | }
1262 | player.velocity.y -= 40
1263 | setTimeout(() => {
1264 | goombas.splice(index, 1)
1265 | }, 0)
1266 | } else if (
1267 | player.position.x + player.width >= goomba.position.x &&
1268 | player.position.y + player.height >= goomba.position.y &&
1269 | player.position.x <= goomba.position.x + goomba.width
1270 | ) {
1271 | // player hits goomba
1272 | // lose fireflower / lose powerup
1273 | if (player.powerUps.fireFlower) {
1274 | player.invincible = true
1275 | player.powerUps.fireFlower = false
1276 | audio.losePowerUp.play()
1277 |
1278 | setTimeout(() => {
1279 | player.invincible = false
1280 | }, 1000)
1281 | } else if (!player.invincible) {
1282 | audio.die.play()
1283 | selectLevel(currentLevel)
1284 | }
1285 | }
1286 | })
1287 |
1288 | player.update()
1289 |
1290 | if (game.disableUserInput) return
1291 |
1292 | // scrolling code starts
1293 | let hitSide = false
1294 | if (keys.right.pressed && player.position.x < 400) {
1295 | player.velocity.x = player.speed
1296 | } else if (
1297 | (keys.left.pressed && player.position.x > 100) ||
1298 | (keys.left.pressed && scrollOffset === 0 && player.position.x > 0)
1299 | ) {
1300 | player.velocity.x = -player.speed
1301 | } else {
1302 | player.velocity.x = 0
1303 |
1304 | // scrolling code
1305 | if (keys.right.pressed) {
1306 | for (let i = 0; i < platforms.length; i++) {
1307 | const platform = platforms[i]
1308 | platform.velocity.x = -player.speed
1309 |
1310 | if (
1311 | platform.block &&
1312 | hitSideOfPlatform({
1313 | object: player,
1314 | platform
1315 | })
1316 | ) {
1317 | platforms.forEach((platform) => {
1318 | platform.velocity.x = 0
1319 | })
1320 |
1321 | hitSide = true
1322 | break
1323 | }
1324 | }
1325 |
1326 | if (!hitSide) {
1327 | scrollOffset += player.speed
1328 |
1329 | flagPole.velocity.x = -player.speed
1330 |
1331 | genericObjects.forEach((genericObject) => {
1332 | genericObject.velocity.x = -player.speed * 0.66
1333 | })
1334 |
1335 | goombas.forEach((goomba) => {
1336 | goomba.position.x -= player.speed
1337 | })
1338 |
1339 | fireFlowers.forEach((fireFlower) => {
1340 | fireFlower.position.x -= player.speed
1341 | })
1342 |
1343 | particles.forEach((particle) => {
1344 | particle.position.x -= player.speed
1345 | })
1346 | }
1347 | } else if (keys.left.pressed && scrollOffset > 0) {
1348 | for (let i = 0; i < platforms.length; i++) {
1349 | const platform = platforms[i]
1350 | platform.velocity.x = player.speed
1351 |
1352 | if (
1353 | platform.block &&
1354 | hitSideOfPlatform({
1355 | object: player,
1356 | platform
1357 | })
1358 | ) {
1359 | platforms.forEach((platform) => {
1360 | platform.velocity.x = 0
1361 | })
1362 |
1363 | hitSide = true
1364 | break
1365 | }
1366 | }
1367 |
1368 | if (!hitSide) {
1369 | scrollOffset -= player.speed
1370 |
1371 | flagPole.velocity.x = player.speed
1372 |
1373 | genericObjects.forEach((genericObject) => {
1374 | genericObject.velocity.x = player.speed * 0.66
1375 | })
1376 |
1377 | goombas.forEach((goomba) => {
1378 | goomba.position.x += player.speed
1379 | })
1380 |
1381 | fireFlowers.forEach((fireFlower) => {
1382 | fireFlower.position.x += player.speed
1383 | })
1384 |
1385 | particles.forEach((particle) => {
1386 | particle.position.x += player.speed
1387 | })
1388 | }
1389 | }
1390 | }
1391 |
1392 | // platform collision detection
1393 | platforms.forEach((platform) => {
1394 | if (
1395 | isOnTopOfPlatform({
1396 | object: player,
1397 | platform
1398 | })
1399 | ) {
1400 | player.velocity.y = 0
1401 | }
1402 |
1403 | if (
1404 | platform.block &&
1405 | hitBottomOfPlatform({
1406 | object: player,
1407 | platform
1408 | })
1409 | ) {
1410 | player.velocity.y = -player.velocity.y
1411 | }
1412 |
1413 | if (
1414 | platform.block &&
1415 | hitSideOfPlatform({
1416 | object: player,
1417 | platform
1418 | })
1419 | ) {
1420 | player.velocity.x = 0
1421 | }
1422 |
1423 | // particles bounce
1424 | particles.forEach((particle, index) => {
1425 | if (
1426 | isOnTopOfPlatformCircle({
1427 | object: particle,
1428 | platform
1429 | })
1430 | ) {
1431 | const bounce = 0.9
1432 | particle.velocity.y = -particle.velocity.y * 0.99
1433 |
1434 | if (particle.radius - 0.4 < 0) particles.splice(index, 1)
1435 | else particle.radius -= 0.4
1436 | }
1437 |
1438 | if (particle.ttl < 0) particles.splice(index, 1)
1439 | })
1440 |
1441 | goombas.forEach((goomba) => {
1442 | if (
1443 | isOnTopOfPlatform({
1444 | object: goomba,
1445 | platform
1446 | })
1447 | )
1448 | goomba.velocity.y = 0
1449 | })
1450 |
1451 | fireFlowers.forEach((fireFlower) => {
1452 | if (
1453 | isOnTopOfPlatform({
1454 | object: fireFlower,
1455 | platform
1456 | })
1457 | )
1458 | fireFlower.velocity.y = 0
1459 | })
1460 | })
1461 |
1462 | // lose condition
1463 | if (player.position.y > canvas.height) {
1464 | audio.die.play()
1465 | selectLevel(currentLevel)
1466 | }
1467 |
1468 | // sprite switching
1469 |
1470 | if (player.shooting) {
1471 | player.currentSprite = player.sprites.shoot.fireFlower.right
1472 |
1473 | if (lastKey === 'left')
1474 | player.currentSprite = player.sprites.shoot.fireFlower.left
1475 |
1476 | return
1477 | }
1478 |
1479 | // sprite jump
1480 | if (player.velocity.y !== 0) return
1481 |
1482 | if (
1483 | keys.right.pressed &&
1484 | lastKey === 'right' &&
1485 | player.currentSprite !== player.sprites.run.right
1486 | ) {
1487 | player.currentSprite = player.sprites.run.right
1488 | } else if (
1489 | keys.left.pressed &&
1490 | lastKey === 'left' &&
1491 | player.currentSprite !== player.sprites.run.left
1492 | ) {
1493 | player.currentSprite = player.sprites.run.left
1494 | } else if (
1495 | !keys.left.pressed &&
1496 | lastKey === 'left' &&
1497 | player.currentSprite !== player.sprites.stand.left
1498 | ) {
1499 | player.currentSprite = player.sprites.stand.left
1500 | } else if (
1501 | !keys.right.pressed &&
1502 | lastKey === 'right' &&
1503 | player.currentSprite !== player.sprites.stand.right
1504 | ) {
1505 | player.currentSprite = player.sprites.stand.right
1506 | }
1507 |
1508 | // fireflower sprites
1509 | if (!player.powerUps.fireFlower) return
1510 |
1511 | if (
1512 | keys.right.pressed &&
1513 | lastKey === 'right' &&
1514 | player.currentSprite !== player.sprites.run.fireFlower.right
1515 | ) {
1516 | player.currentSprite = player.sprites.run.fireFlower.right
1517 | } else if (
1518 | keys.left.pressed &&
1519 | lastKey === 'left' &&
1520 | player.currentSprite !== player.sprites.run.fireFlower.left
1521 | ) {
1522 | player.currentSprite = player.sprites.run.fireFlower.left
1523 | } else if (
1524 | !keys.left.pressed &&
1525 | lastKey === 'left' &&
1526 | player.currentSprite !== player.sprites.stand.fireFlower.left
1527 | ) {
1528 | player.currentSprite = player.sprites.stand.fireFlower.left
1529 | } else if (
1530 | !keys.right.pressed &&
1531 | lastKey === 'right' &&
1532 | player.currentSprite !== player.sprites.stand.fireFlower.right
1533 | ) {
1534 | player.currentSprite = player.sprites.stand.fireFlower.right
1535 | }
1536 | } // animation loop ends
1537 |
1538 | selectLevel(1)
1539 | // init()
1540 | // initLevel2()
1541 | animate()
1542 |
1543 | addEventListener('keydown', ({ keyCode }) => {
1544 | if (game.disableUserInput) return
1545 |
1546 | switch (keyCode) {
1547 | case 65:
1548 | console.log('left')
1549 | keys.left.pressed = true
1550 | lastKey = 'left'
1551 |
1552 | break
1553 |
1554 | case 83:
1555 | console.log('down')
1556 | break
1557 |
1558 | case 68:
1559 | console.log('right')
1560 | keys.right.pressed = true
1561 | lastKey = 'right'
1562 |
1563 | break
1564 |
1565 | case 87:
1566 | console.log('up')
1567 | player.velocity.y -= 25
1568 |
1569 | audio.jump.play()
1570 |
1571 | if (lastKey === 'right') player.currentSprite = player.sprites.jump.right
1572 | else player.currentSprite = player.sprites.jump.left
1573 |
1574 | if (!player.powerUps.fireFlower) break
1575 |
1576 | if (lastKey === 'right')
1577 | player.currentSprite = player.sprites.jump.fireFlower.right
1578 | else player.currentSprite = player.sprites.jump.fireFlower.left
1579 |
1580 | break
1581 |
1582 | case 32:
1583 | console.log('space')
1584 |
1585 | if (!player.powerUps.fireFlower) return
1586 |
1587 | player.shooting = true
1588 |
1589 | setTimeout(() => {
1590 | player.shooting = false
1591 | }, 100)
1592 |
1593 | audio.fireFlowerShot.play()
1594 |
1595 | let velocity = 15
1596 | if (lastKey === 'left') velocity = -velocity
1597 |
1598 | particles.push(
1599 | new Particle({
1600 | position: {
1601 | x: player.position.x + player.width / 2,
1602 | y: player.position.y + player.height / 2
1603 | },
1604 | velocity: {
1605 | x: velocity,
1606 | y: 0
1607 | },
1608 | radius: 5,
1609 | color: 'red',
1610 | fireball: true
1611 | })
1612 | )
1613 | break
1614 | }
1615 | })
1616 |
1617 | addEventListener('keyup', ({ keyCode }) => {
1618 | if (game.disableUserInput) return
1619 |
1620 | switch (keyCode) {
1621 | case 65:
1622 | console.log('left')
1623 | keys.left.pressed = false
1624 | break
1625 |
1626 | case 83:
1627 | console.log('down')
1628 | break
1629 |
1630 | case 68:
1631 | console.log('right')
1632 | keys.right.pressed = false
1633 |
1634 | break
1635 |
1636 | case 87:
1637 | console.log('up')
1638 | break
1639 | }
1640 | })
1641 |
--------------------------------------------------------------------------------
/src/js/images.js:
--------------------------------------------------------------------------------
1 | import backgroundLevel2 from '../img/level2/background.png'
2 | import mountains from '../img/level2/mountains.png'
3 | import lgPlatformLevel2 from '../img/level2/lgPlatform.png'
4 | import mdPlatformLevel2 from '../img/level2/mdPlatform.png'
5 | import spriteFireFlowerShootLeft from '../img/mario/spriteFireFlowerShootLeft.png'
6 | import spriteFireFlowerShootRight from '../img/mario/spriteFireFlowerShootRight.png'
7 |
8 | export const images = {
9 | mario: {
10 | shoot: {
11 | fireFlower: {
12 | right: spriteFireFlowerShootRight,
13 | left: spriteFireFlowerShootLeft
14 | }
15 | }
16 | },
17 | levels: {
18 | 1: {
19 | background: ''
20 | },
21 | 2: {
22 | background: backgroundLevel2,
23 | mountains,
24 | lgPlatform: lgPlatformLevel2,
25 | mdPlatform: mdPlatformLevel2
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/js/utils.js:
--------------------------------------------------------------------------------
1 | function randomIntFromRange(min, max) {
2 | return Math.floor(Math.random() * (max - min + 1) + min)
3 | }
4 |
5 | function randomColor(colors) {
6 | return colors[Math.floor(Math.random() * colors.length)]
7 | }
8 |
9 | function distance(x1, y1, x2, y2) {
10 | const xDist = x2 - x1
11 | const yDist = y2 - y1
12 |
13 | return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2))
14 | }
15 |
16 | export function createImage(imageSrc) {
17 | const image = new Image()
18 | image.src = imageSrc
19 | return image
20 | }
21 |
22 | export function createImageAsync(imageSrc) {
23 | return new Promise((resolve) => {
24 | const image = new Image()
25 | image.onload = () => {
26 | resolve(image)
27 | }
28 | image.src = imageSrc
29 | })
30 | }
31 |
32 | export function isOnTopOfPlatform({ object, platform }) {
33 | return (
34 | object.position.y + object.height <= platform.position.y &&
35 | object.position.y + object.height + object.velocity.y >=
36 | platform.position.y &&
37 | object.position.x + object.width >= platform.position.x &&
38 | object.position.x <= platform.position.x + platform.width
39 | )
40 | }
41 |
42 | export function collisionTop({ object1, object2 }) {
43 | return (
44 | object1.position.y + object1.height <= object2.position.y &&
45 | object1.position.y + object1.height + object1.velocity.y >=
46 | object2.position.y &&
47 | object1.position.x + object1.width >= object2.position.x &&
48 | object1.position.x <= object2.position.x + object2.width
49 | )
50 | }
51 |
52 | export function isOnTopOfPlatformCircle({ object, platform }) {
53 | return (
54 | object.position.y + object.radius <= platform.position.y &&
55 | object.position.y + object.radius + object.velocity.y >=
56 | platform.position.y &&
57 | object.position.x + object.radius >= platform.position.x &&
58 | object.position.x <= platform.position.x + platform.width
59 | )
60 | }
61 |
62 | export function hitBottomOfPlatform({ object, platform }) {
63 | return (
64 | object.position.y <= platform.position.y + platform.height &&
65 | object.position.y - object.velocity.y >=
66 | platform.position.y + platform.height &&
67 | object.position.x + object.width >= platform.position.x &&
68 | object.position.x <= platform.position.x + platform.width
69 | )
70 | }
71 |
72 | export function hitSideOfPlatform({ object, platform }) {
73 | return (
74 | object.position.x +
75 | object.width +
76 | object.velocity.x -
77 | platform.velocity.x >=
78 | platform.position.x &&
79 | object.position.x + object.velocity.x <=
80 | platform.position.x + platform.width &&
81 | object.position.y <= platform.position.y + platform.height &&
82 | object.position.y + object.height >= platform.position.y
83 | )
84 | }
85 |
86 | // two rectangles collide
87 | export function objectsTouch({ object1, object2 }) {
88 | return (
89 | object1.position.x + object1.width >= object2.position.x &&
90 | object1.position.x <= object2.position.x + object2.width &&
91 | object1.position.y + object1.height >= object2.position.y &&
92 | object1.position.y <= object2.position.y + object2.height
93 | )
94 | }
95 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const BrowserSyncPlugin = require('browser-sync-webpack-plugin')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 |
4 | module.exports = {
5 | mode: 'development',
6 | entry: './src/js/canvas.js',
7 | output: {
8 | path: __dirname + '/dist/',
9 | filename: './js/canvas.bundle.js'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.m?js$/,
15 | exclude: /(node_modules|bower_components)/,
16 | use: {
17 | loader: 'babel-loader',
18 | options: {
19 | presets: ['@babel/preset-env']
20 | }
21 | }
22 | },
23 | {
24 | test: /\.(png|jpe?g|gif)$/i,
25 | use: [
26 | {
27 | loader: 'file-loader'
28 | }
29 | ]
30 | },
31 | {
32 | test: /\.(mp3|wav)$/i,
33 | use: [
34 | {
35 | loader: 'file-loader',
36 | options: {
37 | outputPath: 'audio'
38 | }
39 | }
40 | ]
41 | }
42 | ]
43 | },
44 | plugins: [
45 | new BrowserSyncPlugin({
46 | host: 'localhost',
47 | port: 3000,
48 | server: { baseDir: ['dist'] },
49 | files: ['./dist/*'],
50 | notify: false
51 | }),
52 | new HtmlWebpackPlugin({
53 | filename: 'index.html',
54 | favicon: 'favicon.ico',
55 | template: 'src/index.html'
56 | })
57 | ],
58 | watch: true,
59 | devtool: 'source-map'
60 | }
61 |
--------------------------------------------------------------------------------