From a4c866bd89f3abdc62ac978ac445baa999b006e3 Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 22 Mar 2018 22:40:18 +0100 Subject: [PATCH] - Added Angular Material - Added Ubuntu font - Added leaderboards - Small code optimization - Fixed minor bugs --- package-lock.json | 21 ++++++ package.json | 3 + src/app/app.component.html | 6 ++ src/app/app.component.ts | 69 ++++++++++++++----- src/app/app.module.ts | 23 ++++++- src/app/app.states.ts | 2 + src/app/classes/Packet.ts | 31 +++++++-- src/app/hello/hello.component.html | 33 +++++++++ src/app/hello/hello.component.scss | 0 src/app/hello/hello.component.ts | 22 ++++++ .../leaderboard/leaderboard.component.html | 33 +++++++++ .../leaderboard/leaderboard.component.scss | 0 src/app/leaderboard/leaderboard.component.ts | 68 ++++++++++++++++++ src/assets/styles/main.scss | 21 ++++++ src/assets/styles/theming.scss | 11 +++ src/index.html | 3 + src/main.ts | 2 + src/styles.scss | 1 + 18 files changed, 324 insertions(+), 25 deletions(-) create mode 100644 src/app/hello/hello.component.html create mode 100644 src/app/hello/hello.component.scss create mode 100644 src/app/hello/hello.component.ts create mode 100644 src/app/leaderboard/leaderboard.component.html create mode 100644 src/app/leaderboard/leaderboard.component.scss create mode 100644 src/app/leaderboard/leaderboard.component.ts create mode 100644 src/assets/styles/theming.scss diff --git a/package-lock.json b/package-lock.json index be30abd..c0930c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,14 @@ "tslib": "1.9.0" } }, + "@angular/cdk": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.2.4.tgz", + "integrity": "sha1-wKQpqHENj+2xV/VG4hy0nUM19/c=", + "requires": { + "tslib": "1.9.0" + } + }, "@angular/cli": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.7.3.tgz", @@ -201,6 +209,14 @@ "integrity": "sha512-aaLnGpW9NBDkG0JYqUeGc+al1Jd1CY9yrs3mew53x5nByetQbIdZwpYm1hnSTw7LBEZBxfHTMw5EZD2YYTDmJw==", "dev": true }, + "@angular/material": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.2.4.tgz", + "integrity": "sha1-noI3mDJCg9I+qDkVb6xby3NEPVU=", + "requires": { + "tslib": "1.9.0" + } + }, "@angular/platform-browser": { "version": "5.2.9", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.9.tgz", @@ -3846,6 +3862,11 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, "handle-thing": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", diff --git a/package.json b/package.json index 7229af4..2c6be6d 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,18 @@ "private": true, "dependencies": { "@angular/animations": "^5.2.9", + "@angular/cdk": "^5.2.4", "@angular/common": "^5.2.9", "@angular/compiler": "^5.2.9", "@angular/core": "^5.2.9", "@angular/forms": "^5.2.9", "@angular/http": "^5.2.9", + "@angular/material": "^5.2.4", "@angular/platform-browser": "^5.2.9", "@angular/platform-browser-dynamic": "^5.2.9", "@angular/router": "^5.2.9", "core-js": "^2.4.1", + "hammerjs": "^2.0.8", "rxjs": "^5.5.7", "zone.js": "^0.8.19" }, diff --git a/src/app/app.component.html b/src/app/app.component.html index 571e1eb..9a5fefc 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,7 @@ +
+ +
+ \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c570dfe..b0913a7 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,8 @@ import { Component, AfterContentInit, HostListener } from '@angular/core'; +import { MatDialog } from '@angular/material'; + +import { HelloComponent } from './hello/hello.component'; +import { LeaderboardComponent } from './leaderboard/leaderboard.component'; import { AppConstants } from './app.constants'; import { AppStates } from './app.states'; @@ -41,6 +45,9 @@ export class AppComponent implements AfterContentInit { public levelUpSize = 0; public heart; + public helloScreen = true; + + constructor(public dialog: MatDialog) { } ngAfterContentInit() { this.game = document.getElementById('breakout'); @@ -57,6 +64,9 @@ export class AppComponent implements AfterContentInit { packetY = this.bouncer.y - AppConstants.PACKET_RADIUS - 5; this.packet = new Packet(packetX, packetY, this.bouncer, this.context); this.then = Date.now(); + + this.initiateHelloScreen(); + this.gameLoop(); } @@ -68,17 +78,6 @@ export class AppComponent implements AfterContentInit { if (this.elapsed > 1000 / this.gameFPS) { this.then = this.now - (this.elapsed % (1000 / this.gameFPS)); - this.context.clearRect(0, 0, AppConstants.GAME_WIDTH, AppConstants.GAME_HEIGHT + 40); - this.context.fillStyle = 'rgba(16, 16, 16, 0.5)'; - this.context.fillRect(0, 0, AppConstants.GAME_WIDTH, AppConstants.GAME_HEIGHT + 40); - - this.cameraShake(); - - this.context.font = '85px Arial'; - this.context.textAlign = 'center'; - this.context.fillStyle = 'rgba(111, 111, 111, 1.0)'; - this.context.fillText(String(AppStates.STAGE), AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT, AppConstants.GAME_WIDTH); - if (this.gameStarting) { this.levelUpSize -= 0.03; if (this.levelUpSize <= 0) { @@ -119,7 +118,18 @@ export class AppComponent implements AfterContentInit { } } - this.context.font = (Easing.easeInOutCubic(this.levelUpSize) * 50) + 'px Arial'; + this.context.clearRect(0, 0, AppConstants.GAME_WIDTH, AppConstants.GAME_HEIGHT + 40); + this.context.fillStyle = 'rgba(16, 16, 16, 0.5)'; + this.context.fillRect(0, 0, AppConstants.GAME_WIDTH, AppConstants.GAME_HEIGHT + 40); + + // this.cameraShake(); + + this.context.font = '85px Ubuntu'; + this.context.textAlign = 'center'; + this.context.fillStyle = 'rgba(111, 111, 111, 1.0)'; + this.context.fillText(String(AppStates.STAGE), AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT, AppConstants.GAME_WIDTH); + + this.context.font = (Easing.easeInOutCubic(this.levelUpSize) * 50) + 'px Ubuntu'; this.context.fillStyle = 'rgba(255, 255, 255, ' + this.levelUpSize + ')'; this.context.fillText('Level up!', AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT - 130, AppConstants.GAME_WIDTH); @@ -127,11 +137,13 @@ export class AppComponent implements AfterContentInit { this.context.strokeStyle = 'rgba(111, 111, 111, ' + (1 - this.circleExpand) + ')'; this.context.lineWidth = 15; const radius = 70 + 20 * this.circleExpand; - this.context.arc(AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT - 30, radius, 0, Math.PI * this.circleAnimation); + const startAngle = -Math.PI / 2; + const endAngle = startAngle + (Math.PI) * this.circleAnimation; + this.context.arc(AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT - 30, radius, startAngle, endAngle); this.context.stroke(); this.context.closePath(); - this.context.font = '25px Arial'; + this.context.font = '25px Ubuntu'; this.context.fillStyle = 'rgba(255, 255, 255, 1.0)'; if (!this.gameStarting && !AppStates.STARTED) { this.context.fillText('Press SPACE to begin', AppConstants.HALF_WIDTH, AppConstants.HALF_HEIGHT + 100, AppConstants.GAME_WIDTH); @@ -157,9 +169,9 @@ export class AppComponent implements AfterContentInit { this.context.stroke(); this.context.closePath();*/ - if (this.shakeTickStart !== -1) { + /*if (this.shakeTickStart !== -1) { this.restoreCamera(); - } + }*/ } } @@ -168,11 +180,34 @@ export class AppComponent implements AfterContentInit { onKeyEvent(event: KeyboardEvent) { this.bouncer.onKeyChange(event, event.type === 'keydown' ? true : false); - if (event.keyCode === 32) { + if (event.keyCode === 32 && !this.helloScreen) { + // Prevent from firing focused Material element + event.preventDefault(); this.gameStarting = true; } } + initiateHelloScreen() { + setTimeout(() => { + const dialog = this.dialog.open(HelloComponent, { + width: '500px', + disableClose: true + }); + + dialog.afterClosed() + .subscribe((nameInput) => { + AppStates.NAME = nameInput; + this.helloScreen = false; + }); + }); + } + + openLeaderboard() { + const dialog = this.dialog.open(LeaderboardComponent, { + width: '500px' + }); + } + cameraShake() { if (this.shakeTickStart === -1) { return; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 926975a..a7dcf55 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,18 +1,35 @@ import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; +import { + MatButtonModule, + MatDialogModule, + MatIconModule, + MatInputModule +} from '@angular/material'; import { AppComponent } from './app.component'; +import { HelloComponent } from './hello/hello.component'; +import { LeaderboardComponent } from './leaderboard/leaderboard.component'; @NgModule({ declarations: [ - AppComponent + AppComponent, + HelloComponent, + LeaderboardComponent ], imports: [ - BrowserModule + BrowserModule, + BrowserAnimationsModule, + MatButtonModule, + MatDialogModule, + MatIconModule, + MatInputModule ], providers: [], - bootstrap: [AppComponent] + bootstrap: [AppComponent], + entryComponents: [HelloComponent, LeaderboardComponent] }) export class AppModule { } diff --git a/src/app/app.states.ts b/src/app/app.states.ts index 7a747ed..8ef774a 100644 --- a/src/app/app.states.ts +++ b/src/app/app.states.ts @@ -13,4 +13,6 @@ export class AppStates { public static WALLS_WIDTH = AppConstants.GAME_WIDTH / AppStates.WALLS_X; static readonly WALLS_HEIGHT = 40; + + public static NAME = 'guest'; } diff --git a/src/app/classes/Packet.ts b/src/app/classes/Packet.ts index f4b27f5..1a91c91 100644 --- a/src/app/classes/Packet.ts +++ b/src/app/classes/Packet.ts @@ -1,3 +1,5 @@ +import { LeaderboardComponent } from '../leaderboard/leaderboard.component'; + import { AppConstants } from '../app.constants'; import { AppStates } from '../app.states'; import { Drawable } from './Drawable'; @@ -13,6 +15,10 @@ export class Packet extends Drawable { private velocityX = Math.random() * (Math.floor(Math.random() * 2) === 1 ? 1 : -1); private velocityY = -1; + private angle = Math.atan2(this.velocityY, this.velocityX); + private angleX = Math.cos(this.angle); + private angleY = Math.sin(this.angle); + private hitRight = false; private hitLeft = false; private erpRight = 0; @@ -31,9 +37,8 @@ export class Packet extends Drawable { // Trigonometry - calculate direction in radians and multiply with speed // This allows us to keep constant ball speed if (AppStates.STARTED) { - const angle = Math.atan2(this.velocityY, this.velocityX); - this.x += Math.cos(angle) * (AppStates.PACKET_SPEED + AppConstants.PROGRESSIVENESS * (AppStates.STAGE - 1)); - this.y += Math.sin(angle) * (AppStates.PACKET_SPEED + AppConstants.PROGRESSIVENESS * (AppStates.STAGE - 1)); + this.x += this.angleX * (AppStates.PACKET_SPEED + AppConstants.PROGRESSIVENESS * (AppStates.STAGE - 1)); + this.y += this.angleY * (AppStates.PACKET_SPEED + AppConstants.PROGRESSIVENESS * (AppStates.STAGE - 1)); } for (let x = 0; x < AppStates.WALLS_X; x++) { @@ -55,6 +60,7 @@ export class Packet extends Drawable { // Just a safety check this.y = this.bouncer.y - AppConstants.PACKET_RADIUS; this.velocityX = (this.x - this.bouncer.x - AppConstants.BOUNCER_WIDTH / 2) / 100; + this.updateAngle(); if (this.bounces[0]) { this.bounces[0].returnX = this.x; this.bounces[0].returnY = this.y; @@ -70,7 +76,7 @@ export class Packet extends Drawable { this.context.shadowColor = 'black'; this.context.shadowOffsetX = 1; this.context.shadowOffsetY = 1; - this.context.font = '15px Arial'; + this.context.font = '15px Ubuntu'; this.context.textAlign = 'center'; this.context.strokeStyle = 'rgba(255, 255, 255, ' + packetIP.uiAlpha + ')'; @@ -125,6 +131,12 @@ export class Packet extends Drawable { this.context.closePath(); } + updateAngle() { + this.angle = Math.atan2(this.velocityY, this.velocityX); + this.angleX = Math.cos(this.angle); + this.angleY = Math.sin(this.angle); + } + managePacketIP(lost?) { if (this.bounces.length === 1) { const bounce = this.bounces[0]; @@ -191,7 +203,9 @@ export class Packet extends Drawable { this.bouncer.y = AppConstants.GAME_HEIGHT - AppConstants.BOUNCER_HEIGHT - 10; this.x = this.bouncer.x + AppConstants.BOUNCER_WIDTH / 2; this.y = this.bouncer.y - AppConstants.PACKET_RADIUS - 5; - this.bounces.push(new PacketIP(this.bouncer.x + AppConstants.BOUNCER_WIDTH / 2, this.bouncer.y - 5)); + if (!hardReset) { + this.bounces.push(new PacketIP(this.bouncer.x + AppConstants.BOUNCER_WIDTH / 2, this.bouncer.y - 5)); + } } bounceOnIntersection(wall) { @@ -222,6 +236,7 @@ export class Packet extends Drawable { const normalY = packetDistanceY * sideY < 0 ? -1 : 1; this.velocityY = normalY; } + this.updateAngle(); this.manageWallHit(wall); return; // Yes, bounced! } @@ -238,6 +253,7 @@ export class Packet extends Drawable { vectorY = packetDistanceY < 0 ? -1 : 1; this.velocityX = vectorX * sideX / normalize; this.velocityY = vectorY * sideY / normalize; + this.updateAngle(); this.manageWallHit(wall); return; // Yes, bounced! } @@ -245,6 +261,7 @@ export class Packet extends Drawable { bounceOnBoundsIntersection() { if (this.x > AppConstants.GAME_WIDTH - AppConstants.PACKET_RADIUS) { this.velocityX = -this.velocityX; + this.updateAngle(); this.hitRight = true; // Don't bug out! this.x = AppConstants.GAME_WIDTH - AppConstants.PACKET_RADIUS; @@ -253,6 +270,7 @@ export class Packet extends Drawable { if (this.y >= AppConstants.GAME_HEIGHT - AppConstants.PACKET_RADIUS) { this.velocityX = Math.random() * (Math.floor(Math.random() * 2) === 1 ? 1 : -1); this.velocityY = -this.velocityY; + this.updateAngle(); for (let i = 0; i < this.bounces.length; i++) { const packetIP = this.bounces[i]; @@ -265,6 +283,7 @@ export class Packet extends Drawable { if (--Packet.health <= 0) { // Reset game AppStates.STARTED = false; + LeaderboardComponent.saveScore(AppStates.STAGE); AppStates.STAGE = 1; AppStates.WALLS_X = 3; AppStates.WALLS_WIDTH = AppConstants.GAME_WIDTH / AppStates.WALLS_X; @@ -277,6 +296,7 @@ export class Packet extends Drawable { if (this.x < AppConstants.PACKET_RADIUS) { this.velocityX = -this.velocityX; + this.updateAngle(); this.hitLeft = true; // Bug check this.x = AppConstants.PACKET_RADIUS; @@ -284,6 +304,7 @@ export class Packet extends Drawable { if (this.y < AppConstants.PACKET_RADIUS) { this.velocityY = -this.velocityY; + this.updateAngle(); this.managePacketIP(true); // Bug check this.y = AppConstants.PACKET_RADIUS; diff --git a/src/app/hello/hello.component.html b/src/app/hello/hello.component.html new file mode 100644 index 0000000..f1e1d3d --- /dev/null +++ b/src/app/hello/hello.component.html @@ -0,0 +1,33 @@ +

👨‍💻 Welcome to Hacker Breakout!

+ + +

+ You are trapped in a world, where cyber technology is heavily advanced. Everything is controlled by an A.I. + It won't let you leave, so all you can do is compete with others. +

+

+ Everytime you break all walls, you open a door to a new level with higher security and bigger firewall, + therefore you will need higher network speeds. The packet will move faster based on the level you are in. + You only have 3 lives, but you gain a life after levelling up. +

+

+ Move the bouncer (paddle) with ⬅️ and ➡️ keys on your keyboard. You can check leaderboards by clicking + on the ⭐ button on the far left. +

+

+ One more thing: I will need your name. +

+ + + This field is required + +

+ - Timotej M. +

+
+ + + + diff --git a/src/app/hello/hello.component.scss b/src/app/hello/hello.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/hello/hello.component.ts b/src/app/hello/hello.component.ts new file mode 100644 index 0000000..f3df51d --- /dev/null +++ b/src/app/hello/hello.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; + +@Component({ + selector: 'app-hello', + templateUrl: './hello.component.html', + styleUrls: ['./hello.component.scss'] +}) +export class HelloComponent { + public nameError; + + constructor(public dialogRef: MatDialogRef) { } + + confirm(nameInput) { + if (!nameInput.value.trim()) { + this.nameError = true; + return; + } + this.dialogRef.close(nameInput.value); + } + +} diff --git a/src/app/leaderboard/leaderboard.component.html b/src/app/leaderboard/leaderboard.component.html new file mode 100644 index 0000000..6d2a731 --- /dev/null +++ b/src/app/leaderboard/leaderboard.component.html @@ -0,0 +1,33 @@ +

⭐ Top 5 players

+ + +
+ +

There are no scores yet!

+
+ +
    +
  • +

    + 🥇 + 🥈 + 🥉 + 🌟 +
    + + {{score.name}}: {{score.score}} + + + {{score.name}}: {{score.score}} + +

    +
  • +
+
+
+ + + + diff --git a/src/app/leaderboard/leaderboard.component.scss b/src/app/leaderboard/leaderboard.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/leaderboard/leaderboard.component.ts b/src/app/leaderboard/leaderboard.component.ts new file mode 100644 index 0000000..211b640 --- /dev/null +++ b/src/app/leaderboard/leaderboard.component.ts @@ -0,0 +1,68 @@ +import { Component } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; + +import { AppStates } from '../app.states'; + +@Component({ + selector: 'app-leaderboard', + templateUrl: './leaderboard.component.html', + styleUrls: ['./leaderboard.component.scss'] +}) +export class LeaderboardComponent { + public scores = []; + + constructor(public dialogRef: MatDialogRef) { + this.loadScores(); + } + + public static saveScore(newScore) { + // Very badly scripted. I was in a hurry :) + const leaderboard = localStorage.getItem('leaderboard'); + let temporaryScores = []; + if (leaderboard) { + temporaryScores = JSON.parse(leaderboard); + } + let found = false; + for (let i = 0; i < temporaryScores.length; i++) { + const score = temporaryScores[i]; + if (score.name === AppStates.NAME) { + if (newScore > score.score) { + score.score = newScore; + } + found = true; + break; + } + } + if (!found) { + temporaryScores.push({ name: AppStates.NAME, score: newScore }); + } + localStorage.setItem('leaderboard', JSON.stringify(temporaryScores)); + } + + loadScores() { + const rawLeaderboard = localStorage.getItem('leaderboard'); + + let leaderboard = []; + if (rawLeaderboard) { + leaderboard = JSON.parse(rawLeaderboard); + } + + /*for (let i = 0; i < 5; i++) { + this.scores[i] = { name: 'John' + Math.round(Math.random() * 255), score: Math.round(Math.random() * 100) }; + } + this.scores.sort((a, b) => b.score - a.score);*/ + + // Very badly scripted. I was in a hurry :) + leaderboard.sort((a, b) => b.score - a.score); + for (let i = 0; i < 5; i++) { + if (leaderboard[i]) { + this.scores[i] = { name: leaderboard[i].name, score: leaderboard[i].score }; + } + } + } + + getLoggedIn() { + return AppStates.NAME; + } + +} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index 8fa8d1e..6f10705 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -1,5 +1,6 @@ body { overflow: hidden; + font-family: 'Ubuntu', sans-serif; background: url('assets/images/circuit.jpg') no-repeat center center fixed; -webkit-background-size: cover; -moz-background-size: cover; @@ -7,6 +8,22 @@ body { background-size: cover; } +.settings { + position: absolute; + z-index: 900; + top: 50%; + transform: translateY(-50%); +} + +ul.leaderboard { + list-style-type: none; + padding-left: 10px; +} + +.personal { + color: rgb(31, 201, 200); +} + #breakout { position: absolute; margin: auto; @@ -14,4 +31,8 @@ body { bottom: 0; left: 0; right: 0; +} + +mat-form-field { + width: 100%; } \ No newline at end of file diff --git a/src/assets/styles/theming.scss b/src/assets/styles/theming.scss new file mode 100644 index 0000000..01f7385 --- /dev/null +++ b/src/assets/styles/theming.scss @@ -0,0 +1,11 @@ +@import '~@angular/material/theming'; + +@include mat-core(); + +$app-primary: mat-palette($mat-cyan, 700); +$app-accent: mat-palette($mat-blue-grey, 50); +$app-warn: mat-palette($mat-red, A700); + +$app-theme: mat-light-theme($app-primary, $app-accent, $app-warn); + +@include angular-material-theme($app-theme); \ No newline at end of file diff --git a/src/index.html b/src/index.html index a95e418..973136c 100644 --- a/src/index.html +++ b/src/index.html @@ -7,6 +7,9 @@ + + + diff --git a/src/main.ts b/src/main.ts index 91ec6da..a7ea96c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,8 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; +import 'hammerjs'; + if (environment.production) { enableProdMode(); } diff --git a/src/styles.scss b/src/styles.scss index 88f09cc..04cdb03 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,4 +1,5 @@ /* You can add global styles to this file, and also import other style files */ @import 'assets/vendor/styles/normalize.scss'; +@import 'assets/styles/theming.scss'; @import 'assets/styles/main.scss'; \ No newline at end of file