You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/bg/6-space-game/6-end-condition
softchris bde9da6dad
🌐 Update translations via Co-op Translator
1 month ago
..
solution 🌐 Update translations via Co-op Translator 3 months ago
your-work 🌐 Update translations via Co-op Translator 3 months ago
README.md 🌐 Update translations via Co-op Translator 1 month ago
assignment.md 🌐 Update translations via Co-op Translator 1 month ago

README.md

Създаване на космическа игра, част 6: Край и рестарт

Всяка страхотна игра се нуждае от ясни условия за край и плавен механизъм за рестарт. Вече сте създали впечатляваща космическа игра с движение, битки и точки - сега е време да добавите последните елементи, които ще я направят завършена.

Вашата игра в момента продължава безкрайно, подобно на космическите сонди Voyager, които NASA изстреля през 1977 г. и които все още пътуват из космоса десетилетия по-късно. Докато това е подходящо за космическо изследване, игрите се нуждаят от определени крайни точки, за да създадат удовлетворяващо изживяване.

Днес ще внедрим правилни условия за победа/загуба и система за рестарт. До края на този урок ще имате завършена игра, която играчите могат да завършат и да играят отново, точно като класическите аркадни игри, които определиха жанра.

Тест преди лекцията

Тест преди лекцията

Разбиране на условията за край на играта

Кога трябва да приключи вашата игра? Този фундаментален въпрос е оформял дизайна на игрите още от ранната аркадна ера. Pac-Man приключва, когато бъдете хванати от призраци или изчистите всички точки, докато Space Invaders приключва, когато извънземните достигнат дъното или когато ги унищожите всички.

Като създател на играта, вие определяте условията за победа и загуба. За нашата космическа игра, ето доказани подходи, които създават увлекателен геймплей:

  • Унищожени N вражески кораба: Често срещано е, ако разделите играта на различни нива, да трябва да унищожите N вражески кораба, за да завършите нивото.
  • Вашият кораб е унищожен: Има игри, в които губите, ако вашият кораб бъде унищожен. Друг често срещан подход е концепцията за животи. Всеки път, когато вашият кораб бъде унищожен, се отнема един живот. Когато всички животи бъдат загубени, играта приключва.
  • Събрани са N точки: Друго често срещано условие за край е събирането на точки. Как ще събирате точки зависи от вас, но често се присъждат точки за различни дейности като унищожаване на вражески кораб или събиране на предмети, които падат, когато те бъдат унищожени.
  • Завършване на ниво: Това може да включва няколко условия, като унищожени X вражески кораба, събрани Y точки или може би събиране на конкретен предмет.

Внедряване на функционалност за рестарт на играта

Добрите игри насърчават повторяемостта чрез плавни механизми за рестарт. Когато играчите завършат играта (или претърпят поражение), те често искат веднага да опитат отново - било то за да подобрят резултата си или представянето си.

Tetris е перфектен пример за това: когато блоковете достигнат върха, можете веднага да започнете нова игра, без да се налага да навигирате през сложни менюта. Ще изградим подобна система за рестарт, която чисто ще нулира състоянието на играта и ще върне играчите обратно в действие бързо.

Размисъл: Помислете за игрите, които сте играли. При какви условия те приключват и как ви подканват да рестартирате? Какво прави изживяването при рестарт гладко, а не разочароващо?

Какво ще изградите

Ще внедрите финалните функции, които ще превърнат вашия проект в завършено игрово изживяване. Тези елементи отличават полирани игри от основни прототипи.

Ето какво ще добавим днес:

  1. Условие за победа: Унищожете всички врагове и получете подходящо празненство (заслужавате го!)
  2. Условие за загуба: Загубете всички животи и се изправете пред поражението с екран за загуба
  3. Механизъм за рестарт: Натиснете Enter, за да се върнете веднага - защото една игра никога не е достатъчна
  4. Управление на състоянието: Чисто начало всеки път - без остатъчни врагове или странни грешки от предишната игра

Да започваме

Нека подготвим вашата среда за разработка. Трябва да имате всички файлове на вашата космическа игра от предишните уроци.

Вашият проект трябва да изглежда приблизително така:

-| assets
  -| enemyShip.png
  -| player.png
  -| laserRed.png
  -| life.png
-| index.html
-| app.js
-| package.json

Стартирайте вашия сървър за разработка:

cd your-work
npm start

Тази команда:

  • Стартира локален сървър на http://localhost:5000
  • Правилно обслужва вашите файлове
  • Автоматично се обновява, когато правите промени

Отворете http://localhost:5000 в браузъра си и проверете дали играта ви работи. Трябва да можете да се движите, стреляте и взаимодействате с враговете. След като потвърдите, можем да продължим с внедряването.

💡 Полезен съвет: За да избегнете предупреждения в Visual Studio Code, декларирайте gameLoopId в началото на вашия файл като let gameLoopId; вместо да го декларирате вътре във функцията window.onload. Това следва съвременните най-добри практики за деклариране на променливи в JavaScript.

Стъпки за внедряване

Стъпка 1: Създаване на функции за проследяване на условията за край

Трябва да създадем функции, които да следят кога играта трябва да приключи. Подобно на сензорите на Международната космическа станция, които постоянно следят критичните системи, тези функции ще проверяват състоянието на играта.

function isHeroDead() {
  return hero.life <= 0;
}

function isEnemiesDead() {
  const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
  return enemies.length === 0;
}

Ето какво се случва зад кулисите:

  • Проверява дали нашият герой е загубил всички животи (ау!)
  • Брои колко врагове все още са живи и активни
  • Връща true, когато бойното поле е изчистено от врагове
  • Използва проста логика true/false за по-лесно разбиране
  • Филтрира всички игрови обекти, за да намери оцелелите

Стъпка 2: Актуализиране на обработката на събития за условията за край

Сега ще свържем тези проверки на условията с системата за събития на играта. Всеки път, когато се случи сблъсък, играта ще оцени дали той предизвиква условие за край. Това създава незабавна обратна връзка за критични събития в играта.

eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
    first.dead = true;
    second.dead = true;
    hero.incrementPoints();

    if (isEnemiesDead()) {
      eventEmitter.emit(Messages.GAME_END_WIN);
    }
});

eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
    enemy.dead = true;
    hero.decrementLife();
    if (isHeroDead())  {
      eventEmitter.emit(Messages.GAME_END_LOSS);
      return; // loss before victory
    }
    if (isEnemiesDead()) {
      eventEmitter.emit(Messages.GAME_END_WIN);
    }
});

eventEmitter.on(Messages.GAME_END_WIN, () => {
    endGame(true);
});
  
eventEmitter.on(Messages.GAME_END_LOSS, () => {
  endGame(false);
});

Какво се случва тук:

  • Лазер удря враг: И двамата изчезват, получавате точки и проверяваме дали сте спечелили
  • Враг удря вас: Губите живот и проверяваме дали все още сте "живи"
  • Умно подреждане: Първо проверяваме за поражение (никой не иска да спечели и да загуби едновременно!)
  • Моментални реакции: Веднага щом се случи нещо важно, играта го отчита

Стъпка 3: Добавяне на нови константи за съобщения

Трябва да добавите нови типове съобщения към вашия обект Messages. Тези константи помагат за поддържане на последователност и предотвратяване на грешки в системата за събития.

GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",

В горното сме:

  • Добавили константи за събития, свързани с края на играта, за да поддържаме последователност
  • Използвали описателни имена, които ясно указват целта на събитието
  • Следвали съществуващата конвенция за именуване на типове съобщения

Стъпка 4: Внедряване на контроли за рестарт

Сега ще добавите клавишни контроли, които позволяват на играчите да рестартират играта. Клавишът Enter е естествен избор, тъй като обикновено се свързва с потвърждаване на действия и стартиране на нови игри.

Добавете разпознаване на клавиша Enter към съществуващия слушател на събития за натискане на клавиши:

else if(evt.key === "Enter") {
   eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}

Добавете новата константа за съобщение:

KEY_EVENT_ENTER: "KEY_EVENT_ENTER",

Какво трябва да знаете:

  • Разширява съществуващата система за обработка на събития от клавиатурата
  • Използва клавиша Enter като тригер за рестарт за интуитивно потребителско изживяване
  • Излъчва персонализирано събитие, което други части на играта могат да слушат
  • Поддържа същия модел като другите клавишни контроли

Стъпка 5: Създаване на система за показване на съобщения

Вашата игра трябва ясно да комуникира резултатите на играчите. Ще създадем система за съобщения, която показва състоянията на победа и загуба с цветно кодирани текстове, подобно на интерфейсите на терминалите на ранните компютърни системи, където зелено означава успех, а червено сигнализира за грешки.

Създайте функцията displayMessage():

function displayMessage(message, color = "red") {
  ctx.font = "30px Arial";
  ctx.fillStyle = color;
  ctx.textAlign = "center";
  ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}

Стъпка по стъпка, ето какво се случва:

  • Задава размера и стила на шрифта за ясен и четлив текст
  • Прилага цветен параметър с "червено" като стандартен за предупреждения
  • Центрира текста хоризонтално и вертикално върху платното
  • Използва съвременни JavaScript параметри по подразбиране за гъвкави цветови опции
  • Използва 2D контекста на платното за директно рендиране на текст

Създайте функцията endGame():

function endGame(win) {
  clearInterval(gameLoopId);

  // Set a delay to ensure any pending renders complete
  setTimeout(() => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    if (win) {
      displayMessage(
        "Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
        "green"
      );
    } else {
      displayMessage(
        "You died !!! Press [Enter] to start a new game Captain Pew Pew"
      );
    }
  }, 200)  
}

Какво прави тази функция:

  • Замразява всичко на място - край на движението на кораби и лазери
  • Прави малка пауза (200ms), за да позволи на последния кадър да се нарисува
  • Изчиства екрана и го боядисва в черно за драматичен ефект
  • Показва различни съобщения за победители и губещи
  • Кодира новините с цветове - зелено за добро, червено за... е, не толкова добро
  • Уведомява играчите как да се върнат в играта

Стъпка 6: Внедряване на функционалност за рестарт на играта

Системата за рестарт трябва напълно да изчисти текущото състояние на играта и да инициализира нова игрова сесия. Това гарантира, че играчите започват начисто, без остатъчни данни от предишната игра.

Създайте функцията resetGame():

function resetGame() {
  if (gameLoopId) {
    clearInterval(gameLoopId);
    eventEmitter.clear();
    initGame();
    gameLoopId = setInterval(() => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      drawPoints();
      drawLife();
      updateGameObjects();
      drawGameObjects(ctx);
    }, 100);
  }
}

Нека разберем всяка част:

  • Проверява дали текущо работи игрови цикъл, преди да го рестартира
  • Изчиства съществуващия игрови цикъл, за да спре текущата игрова активност
  • Премахва всички слушатели на събития, за да предотврати изтичане на памет
  • Реинициализира състоянието на играта с нови обекти и променливи
  • Стартира нов игрови цикъл с всички основни функции на играта
  • Поддържа същия интервал от 100ms за последователна производителност на играта

Добавете обработка на събитие за клавиша Enter към вашата функция initGame():

eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
  resetGame();
});

Добавете метода clear() към вашия клас EventEmitter:

clear() {
  this.listeners = {};
}

Основни точки за запомняне:

  • Свързва натискането на клавиша Enter с функционалността за рестарт на играта
  • Регистрира този слушател на събития по време на инициализацията на играта
  • Осигурява чист начин за премахване на всички слушатели на събития при рестарт
  • Предотвратява изтичане на памет чрез изчистване на обработчиците на събития между игрите
  • Нулира обекта listeners до празно състояние за нова инициализация

Поздравления! 🎉

👽 💥 🚀 Успешно създадохте завършена игра от нулата. Както програмистите, които създадоха първите видеоигри през 70-те години, вие превърнахте редове код в интерактивно изживяване с правилни игрови механики и обратна връзка към потребителя. 🚀 💥 👽

Постигнахте:

  • Внедрихте пълни условия за победа и загуба с обратна връзка към потребителя
  • Създадохте плавна система за рестарт за непрекъснат геймплей
  • Проектирахте ясна визуална комуникация за състоянията на играта
  • Управлявахте сложни преходи на състоянието на играта и изчистване
  • Сглобихте всички компоненти в един цялостен, играем проект

Предизвикателство с GitHub Copilot Agent 🚀

Използвайте режима Agent, за да завършите следното предизвикателство:

Описание: Подобрете космическата игра, като внедрите система за прогресия на нивата с увеличаваща се трудност и бонус функции.

Задача: Създайте система за многоневова космическа игра, в която всяко ниво има повече вражески кораби с увеличена скорост и здраве. Добавете множител за точки, който се увеличава с всяко ниво, и внедрете бонуси (като бърза стрелба или щит), които се появяват случайно, когато враговете бъдат унищожени. Включете бонус за завършване на ниво и показвайте текущото ниво на екрана заедно със съществуващите точки и животи.

Научете повече за режим Agent тук.

🚀 Допълнително предизвикателство за подобрение

Добавете аудио към вашата игра: Подобрете игровото изживяване, като внедрите звукови ефекти! Помислете за добавяне на аудио за:

  • Лазерни изстрели, когато играчът стреля
  • Унищожаване на врагове, когато корабите бъдат ударени
  • Щети на героя, когато играчът получава удари
  • Победна музика, когато играта е спечелена
  • Звук за поражение, когато играта е загубена

Пример за внедряване на аудио:

// Create audio objects
const laserSound = new Audio('assets/laser.wav');
const explosionSound = new Audio('assets/explosion.wav');

// Play sounds during game events
function playLaserSound() {
  laserSound.currentTime = 0; // Reset to beginning
  laserSound.play();
}

Какво трябва да знаете:

  • Създава аудио обекти за различни звукови ефекти
  • Нулира currentTime, за да позволи бързо възпроизвеждане на звукови ефекти
  • Обработва политики за автоматично възпроизвеждане на браузъра, като задейства звуци от взаимодействия на потребителя
  • Управлява силата на звука и времето за по-добро игрово изживяване

💡 Ресурс за обучение: Разгледайте този [аудио пясъчник](https://www.w3


Отказ от отговорност:
Този документ е преведен с помощта на AI услуга за превод Co-op Translator. Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.