diff --git a/images/file.png b/images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..4c434ff2a1cb4eae07060b9f04c98d1e1d326550 Binary files /dev/null and b/images/file.png differ diff --git a/index.html b/index.html index a87ca0a77a52ca7e78f422d60070549cc9bf0caf..97a211f2a511b6734a0f565d97b6da136df8c6af 100644 --- a/index.html +++ b/index.html @@ -29,16 +29,17 @@ <!-- Hintergrundbild des Spiels --> <div class="background" id="background"></div> - + <div class="mascot" id="mascot"></div> <!-- Mascot hinzugefügt --> + <div class="speech-bubble" id="speechBubble"></div> <!-- Bild des Vogels --> <img src="images/Bird.png" alt="bird-img" class="bird" id="bird-1"> <!-- Bild des Mascots --> - <div class="mascot" id="mascot"> - <img src="images/Tiger-Laecheln.png" alt="Tiger" id="Tiger-Laecheln" class="mascot"> - </div> - - <div class="GameOverMessage"> + <!-- Bild eines Cursors --> + <img id="cursor" src= "images/file.png" + width="15" height="20" class="cursor"> + <p> + <div class="GameOverMessage" id="gameover"> <p id="gameOverText"></p> </div> @@ -55,12 +56,15 @@ <option value="normal">Normal</option> <option value="schnell">Schnell</option> </select> - </div> + </div> + + <div class="start-container"> <button class="start-button" id="start-button">Start</button> <!-- Eingabefeld für den Spielernamen --> <input type="text" id="playerNameInput" placeholder="Enter your name" class="inputField"> - </div> + </div> + </body> </html> diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7b3719c74e597d95459a51cad8906acc39caf8d2 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "flappy-birds-game", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://gitlab.reutlingen-university.de/frontend-development-ki-marketing/prototyping/flappy-birds-game.git" + }, + "private": true, + "dependencies": { + "mqtt": "^5.7.0", + "ws": "^8.17.0" + } +} diff --git a/script.js b/script.js index 252c112cbc7cd31a13107896210d066ad7c2b11f..716dd22cdbf844724a73afab6b96610a9d1f5441 100644 --- a/script.js +++ b/script.js @@ -1,3 +1,4 @@ + // Wähle das Vogel-Element und das Bild aus let bird = document.querySelector('.bird'); let img = document.getElementById('bird-1'); @@ -11,55 +12,146 @@ let score_val = document.querySelector('.score_val'); let message = document.querySelector('.GameOverMessage'); let score_title = document.querySelector('.score_title'); +let bird_dy = 0; +let difficulty = 'schnell'; +let gravity = 0; +let move_speed = 0; +let iPipeseperation= 0; +let jumpheight = 0 + +let realX = 1 +let realY = 1 +let oldY = 0 +let birdflag = false + +const mascot = document.getElementById("mascot"); +const speechBubble = document.getElementById("speechBubble"); +//Mascot +let speaking = false; +let mouthInterval; +let textIndex = 0; +let currentText = ''; +let showNextLetterTimeout; // Timeout-Variable für die showNextLetter-Funktion +// Verbinde mit Websocket +const ws = new WebSocket("ws://localhost:453/ws"); + +//Cursor +let cursor = document.getElementById("cursor"); +let flag = false; +let click = null + +ws.onmessage = function (event){ + const data = JSON.parse(event.data); + realY= window.innerHeight * data.y; + realX= window.innerWidth * data.x; + click = data.click + + cursor.style.left = (realX) + "px"; + cursor.style.top = (realY) + "px"; + if(click && !flag){ + const Element = document.elementFromPoint(realX, realY); + if (Element) { + simulateClick(Element) + } + } + }; + + // Setze den Spielzustand auf "Start" und verstecke das Vogelbild let game_state = 'Start'; img.style.display = 'none'; message.classList.add('messageStyle'); -// Führe den folgenden Code aus, sobald das DOM geladen ist -document.addEventListener('DOMContentLoaded', () => { - const mascot = document.getElementById('mascot'); - const mascotImage = document.getElementById('Tiger-Laecheln'); - - const messages = [ - "Willkommen zum Flappy Birds Spiel! Drücke Enter, um zu starten.", - "Weiche den Hindernissen aus, um Punkte zu sammeln.", - "Viel Glück und hab Spaß!" - ]; - - const images = [ - "images/Tiger-Laecheln.png", - "images/Mund-Geschlossen.png", - "images/Mund-Offen.png" - ]; - - let messageIndex = 0; - let imageIndex = 0; - - // Funktion, um eine Nachricht auszusprechen - function speakMessage(message) { - const utterance = new SpeechSynthesisUtterance(message); - utterance.lang = 'de-DE'; - window.speechSynthesis.speak(utterance); + +function setSpeech() { + return new Promise( + function (resolve, reject) { + let synth = window.speechSynthesis; + let id; + + id = setInterval(() => { + if (synth.getVoices().length !== 0) { + resolve(synth.getVoices()); + clearInterval(id); + } + }, 10); + } + ) + } + function speak(text) { + speaking = true; + currentText = text; + textIndex = 0; + const speechBubble = document.getElementById("speechBubble"); + speechBubble.textContent = ''; // Leeren Sie den Text in der Sprechblase + clearInterval(mouthInterval); + mouthInterval = setInterval(toggleMouth, 500); // Ändern Sie die Zeitintervalle je nach Bedarf + showNextLetterTimeout = setTimeout(showNextLetter, 50); // Timeout für die showNextLetter-Funktion starten } - // Funktion, um die Nachricht zu aktualisieren - function updateMessage() { - const messageText = messages[messageIndex]; - speakMessage(messageText); - messageIndex = (messageIndex + 1) % messages.length; + function showNextLetter() { + const speechBubble = document.getElementById("speechBubble"); + if (textIndex < currentText.length) { + speechBubble.textContent += currentText.charAt(textIndex); + textIndex++; + showNextLetterTimeout = setTimeout(showNextLetter, 50); // Timer für die nächste Buchstabe anzeigen + } else { + speaking = false; + clearInterval(mouthInterval); + resetMouth(); + } } - // Funktion, um das Bild zu aktualisieren - function updateImage() { - mascotImage.src = images[imageIndex]; - imageIndex = (imageIndex + 1) % images.length; + function toggleMouth() { + mascot.classList.toggle('mouth-open'); + mascot.classList.toggle('mouth-closed'); } - // Zeige die nächste Nachricht alle 3 Sekunden an - const messageInterval = setInterval(updateMessage, 3000); - // Wechsle das Bild alle 0.5 Sekunden, um die Mundbewegung zu simulieren - const imageInterval = setInterval(updateImage, 500); + function resetMouth() { + + mascot.classList.remove('mouth-open'); + mascot.classList.remove('mouth-closed'); + } + + document.getElementById("mascot").addEventListener("click", function() { + + if (!speaking) { + speechSynthesis.cancel(); + speak("Willkommen zu Flappy Birds! Deine Hand wird als Cursor erkannt, nutze diese um auf Start zu drücken." + + " Weiche den Hindernissen aus indem du mit deiner Hand den Vogel springen lässt. Pass auf, falls der Vogel sich " + + "den Kopf stößt oder gegen eine der Röhren fliegt, ist es vorbei!"); + let s = setSpeech(); + let speech = new SpeechSynthesisUtterance(); + console.log(window.speechSynthesis.getVoices()) + s.then((voices) => { + //const voices = window.speechSynthesis.getVoices(); + console.log(voices.findIndex(a => a.lang === "de-DE")); + speech.voice = voices[voices.findIndex(a => a.lang === "de-DE")]; + speech.text = currentText; + speech.lang = "de-DE"; + speech.volume = 1; + speech.voiceURI = "native"; + speechSynthesis.cancel(); + window.speechSynthesis.speak(speech); + speechBubble.style.display = "block"; // Sprechblase anzeigen + }); + + } else { + speechSynthesis.cancel(); + speaking = false; + clearInterval(mouthInterval); + resetMouth(); + speechBubble.style.display = "none"; // Sprechblase ausblenden + clearTimeout(showNextLetterTimeout); // Timeout für die showNextLetter-Funktion löschen + } + }); + + + + +// Führe den folgenden Code aus, sobald das DOM geladen ist +document.addEventListener('DOMContentLoaded', () => { + const playerNameInput = document.getElementById('playerNameInput'); const scoresButton = document.getElementById('scoresButton'); const scoreBoard = document.getElementById('scoreBoard'); @@ -67,40 +159,76 @@ document.addEventListener('DOMContentLoaded', () => { let scores = JSON.parse(localStorage.getItem('scores')) || []; // Event-Listener für den Mausklick auf den Start-Button - + + const startButton = document.getElementById('start-button'); startButton.addEventListener('click', () => { if (game_state !== 'Play') { - // Entferne alle Rohre, zeige das Vogelbild, setze die Startposition des Vogels und starte das Spiel - document.querySelectorAll('.pipe_sprite').forEach((e) => { - e.remove(); - }); - // Stoppe die Nachrichten- und Bildaktualisierungsintervalle - clearInterval(messageInterval); - clearInterval(imageInterval); - window.speechSynthesis.cancel(); - img.style.display = 'block'; - bird.style.top = '40vh'; - game_state = 'Play'; message.innerHTML = ''; score_title.innerHTML = 'Score : '; score_val.innerHTML = '0'; message.classList.remove('messageStyle'); startButton.style.display = 'none'; - mascot.style.display ='none'; - difficultyContainer.style.display ='none'; play(); // Starte das Spiel - } + } } )}); +function simulateClick(element) { + if (element) { + element.click(); // Klickereignis auf der derzeitigen cardElement +}} + +function setRestartButton(){ + var container = document.getElementById("gameover") + const restartButton = document.createElement('button'); + restartButton.textContent = "Play again!"; + restartButton.id = "restartButton"; // Eindeutige ID für den Neustart-Button + restartButton.classList.add("start-button") + restartButton.addEventListener('click', play); + container.appendChild(restartButton); +} // Hauptspiellogik function play() { - // Funktion für die Bewegung des Vogels + game_state = 'Play'; + // Entferne alle Rohre, zeige das Vogelbild, setze die Startposition des Vogels und starte das Spiel + document.querySelectorAll('.pipe_sprite').forEach((e) => { + e.remove(); + }); + // Stoppe die Nachrichten- und Bildaktualisierungsintervalle + img.style.display = 'block'; + bird.style.top = '40vh'; + if (difficulty === 'langsam' || difficulty === 'normal' || difficulty === 'schnell') { + + //Passe die Bewegungsgeschwindigkeit basierend auf dem Schwierigkeitsgrad an + if (difficulty === 'langsam') { + move_speed = 1; + gravity = 0.01; + iPipeseperation = 600; + jumpheight = -1.6; + } + else if (difficulty === 'normal') { + move_speed = 2.5; + gravity = 0.03; + iPipeseperation = 190; + jumpheight = -3.6; + } + else if (difficulty === 'schnell') { + move_speed = 3.5; + gravity = 0.05; + iPipeseperation = 130; + jumpheight = -5.6; + } + + requestAnimationFrame(create_pipe); + + } function move() { - if (game_state != 'Play') return; + if (game_state !== 'Play') { + return; + } // Überprüfe Kollisionen mit den Rohren und Spielende let pipe_sprite = document.querySelectorAll('.pipe_sprite'); pipe_sprite.forEach((element) => { @@ -110,7 +238,8 @@ function play() { // Kollisionserkennung if (pipe_sprite_props.right <= 0) { element.remove(); - } else { + } + else { if (bird_props.left < pipe_sprite_props.left + pipe_sprite_props.width && bird_props.left + bird_props.width > pipe_sprite_props.left && bird_props.top < pipe_sprite_props.top + pipe_sprite_props.height && @@ -119,7 +248,7 @@ function play() { document.querySelector('.GameOverMessage').innerHTML = 'Game Over'.fontcolor('red'); message.classList.add('GameOverMessageStyle'); img.style.display = 'none'; - return; + setRestartButton() } else { if (pipe_sprite_props.right < bird_props.left && pipe_sprite_props.right + move_speed >= bird_props.left && @@ -135,24 +264,26 @@ function play() { requestAnimationFrame(move); // Funktion für die Anwendung der Gravitation auf den Vogel - let bird_dy = 0; + function apply_gravity() { if (game_state != 'Play') return; bird_dy += gravity; - // Steuere den Vogelsprung durch Tastendruck - document.addEventListener('keydown', (e) => { - if (e.key == 'ArrowUp' || e.key == ' ') { - img.src = 'images/Bird-2.png'; - bird_dy = -10.6; - } - }); - - document.addEventListener('keyup', (e) => { - if (e.key == 'ArrowUp' || e.key == ' ') { + if ((realY + 50) < oldY && !birdflag) { + birdflag = true + bird_dy = jumpheight; + setTimeout(() => { img.src = 'images/Bird.png'; - } - }); + }, "300"); + img.src = 'images/Bird-2.png'; + setTimeout(()=> { + birdflag = false + }, "400") + + } + + oldY = realY + // Überprüfe, ob der Vogel den Bildschirmrand berührt und das Spiel beendet if (bird_props.top <= 0 || bird_props.bottom >= background.bottom) { @@ -160,6 +291,7 @@ function play() { message.style.left = '28vw'; window.location.reload(); message.classList.remove('GameOverMessageStyle'); + setRestartButton() return; } // Aktualisiere die Vogelposition basierend auf der Gravitation @@ -172,14 +304,14 @@ function play() { // Funktion zum Erstellen und Bewegen der Rohre let pipe_separation = 0; let pipe_gap = 35; - let difficulty = 'normal'; - function create_pipe() { + + function create_pipe() { // Überprüfe, ob das Spiel läuft - if (game_state != 'Play') return; + if (game_state !== 'Play') return; // Erzeuge neue Rohre, wenn der Abstand groß genug ist - if (pipe_separation > 115) { + if (pipe_separation > iPipeseperation) { pipe_separation = 0; // Zufällige Position für das obere Rohr berechnen let pipe_posi = Math.floor(Math.random() * 43) + 8; @@ -209,21 +341,5 @@ function play() { requestAnimationFrame(create_pipe); } - //Starte die Rohrerstellung basierend auf dem Schwierigkeitsgrad - if (difficulty === 'langsam' || difficulty === 'normal' || difficulty === 'schnell') { - requestAnimationFrame(create_pipe); - //Passe die Bewegungsgeschwindigkeit basierend auf dem Schwierigkeitsgrad an - if (difficulty === 'langsam') { - move_speed = 2; - gravity = 0.2; - } - else if (difficulty === 'normal') { - move_speed = 3; - gravity = 0.3; - } - else if (difficulty === 'schnell') { - move_speed = 5; - gravity = 0.6; - } - } + } diff --git a/server.js b/server.js new file mode 100644 index 0000000000000000000000000000000000000000..4acd0d6d9194e0446b88dce30fbec15a6fda4e54 --- /dev/null +++ b/server.js @@ -0,0 +1,30 @@ +const mqtt = require("mqtt"); +const ws = require ("ws"); + +let socketserver = new ws.WebSocketServer({ + port: 453 +}); + + let client = mqtt.connect("mqtt://localhost:1883",{ + username: "standardUser", + password: "GreatHHZ4Ever!" + }); +client.on("connect", function() { + let topicID = "Topic1"; + client.subscribe(topicID); +}); +socketserver.on('connection', ws => { + console.log("new client connected"); + +}); +socketserver.on('close', () => console.log('Client has disconnected!')); + + + + + +client.on("message", function(topic, message){ + socketserver.clients.forEach(client => { + client.send(message.toString()); + console.log(message.toString()) +})}); diff --git a/style.css b/style.css index cdc496f87b1280701e49fbcee4a5241ebbf14b57..b5c3108cf1a692bc1beefa48a6a833ee4c1e9fa9 100644 --- a/style.css +++ b/style.css @@ -4,6 +4,14 @@ padding: 0; box-sizing: border-box; font-family: Arial, Helvetica, sans-serif; + +} +.cursor { + pointer-events: none; + /* doing this makes sure .elementFromPoint + do not acquires the image cursor object */ + position: absolute; + z-index: 100000; } /* Stil für das Eingabefeld */ .inputField { @@ -27,13 +35,12 @@ .start-button { background-color: #3498db; color: #fff; - padding: 10px 20px; - font-size: 16px; + padding: 27px 47px; border: none; - cursor: pointer; border-radius: 5px; z-index: 1000; margin-top: 10px; + font-size: xx-large; } .start-button:hover { @@ -103,32 +110,6 @@ z-index: 100; } -/* Stil für das Maskottchen */ -.mascot { - width: 450px; - height: 500px; - cursor: pointer; - position: fixed; - top: 40vh; - left: -5vw; - z-index: 100; - background-image: url('Tiger-Laecheln.png'); /* Standard-Hintergrundbild */ - background-repeat: no-repeat; - background-size: contain; -} - -.Mund-Offen { - background-image: url('Mund-Offen.png'); /* Hintergrundbild für offenen Mund */ -} - -.Mund-Geschlossen { - background-image: url('Mund-Geschlossen.png'); /* Hintergrundbild für geschlossenen Mund */ -} - -.mascot-text { - visibility: hidden; -} - /* Stil für die Rohre */ .pipe_sprite { @@ -159,6 +140,8 @@ padding: 30px; box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; border-radius: 5%; + display: flex; + flex-direction: column; } @@ -203,3 +186,53 @@ width: 14vw; } } + +.restart-container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; /* Höhe des Containers auf die volle Höhe des Viewports setzen */ +} + +.mascot { + width: 300px; + height: 300px; + position: absolute; + top: calc(40% + 90px); /* Etwas weiter unten als im Hangman-Spiel */ + left: 0; /* Position wie im Hangman-Spiel */ + background-image: url('images/Tiger-Laecheln.png'); + background-size: cover; +} + +.mouth-open { + background-image: url('images/Mund-Offen.png') !important; +} + +.mouth-closed { + background-image: url('images/Mund-Geschlossen.png') !important; +} + +.speech-bubble { + position: absolute; + width: 250px; + padding: 10px; + background-color: #ffffff; + border-radius: 10px; + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); + display: none; + z-index: 2; + top: calc(40% - 120px); /* Etwas weiter unten als im Hangman-Spiel */ + left: 35px; /* Position wie im Hangman-Spiel */ + font-family: Arial, sans-serif; + text-align: center; /* Text horizontal zentriert */ +} +.restart-button { + background-color: darkred; + color: black; + padding: 50px 30px; + font-size: 16px; + border: none; + border-radius: 5px; + z-index: 1000; + margin-top: 10px; +}