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/sr/6-space-game/3-moving-elements-around/README.md

32 KiB

Изградња свемирске игре, део 3: Додавање кретања

Размислите о вашим омиљеним играма оно што их чини привлачним нису само лепа графика, већ начин на који се све креће и реагује на ваше акције. Тренутно, ваша свемирска игра је као лепа слика, али сада ћемо додати кретање које ће је оживети.

Када су НАСА-ини инжењери програмирали рачунар за навигацију за мисије Аполо, суочили су се са сличним изазовом: како учинити да свемирски брод реагује на команде пилота, а истовремено аутоматски одржава корекције курса? Принципи које ћемо данас научити одражавају те исте концепте управљање кретањем које контролише играч уз аутоматско понашање система.

У овом лекцији, научићете како да свемирски бродови клизе преко екрана, реагују на команде играча и стварају глатке обрасце кретања. Разложићемо све на разумљиве концепте који се природно надовезују један на други.

На крају, играчи ће моћи да управљају својим херојским бродом по екрану, док ће непријатељски бродови патролирати изнад. Још важније, разумећете основне принципе који покрећу системе кретања у играма.

Квиз пре предавања

Квиз пре предавања

Разумевање кретања у игри

Игре оживљавају када се ствари почну кретати, а постоје два основна начина на која се то дешава:

  • Кретање које контролише играч: Када притиснете тастер или кликнете мишем, нешто се помера. Ово је директна веза између вас и света игре.
  • Аутоматско кретање: Када сама игра одлучује да помера ствари попут оних непријатељских бродова који морају патролирати екраном без обзира на ваше акције.

Померање објеката на екрану рачунара је једноставније него што мислите. Сећате се оних x и y координата из математике? Управо са тим радимо овде. Када је Галилео пратио Јупитерове месеце 1610. године, он је у суштини радио исто цртао је позиције током времена да би разумео обрасце кретања.

Померање ствари на екрану је као стварање анимације у стилу флипбука потребно је следити ова три једноставна корака:

  1. Ажурирајте позицију Промените где ваш објекат треба да буде (можда га померите 5 пиксела удесно)
  2. Обришите стари кадар Очистите екран како не бисте видели трагове
  3. Нацртајте нови кадар Поставите ваш објекат на нову позицију

Радите ово довољно брзо, и ето! Добијате глатко кретање које изгледа природно играчима.

Ево како то може изгледати у коду:

// Set the hero's location
hero.x += 5;
// Clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);

Шта овај код ради:

  • Ажурира x-координату хероја за 5 пиксела како би се померао хоризонтално
  • Брише целу област платна како би уклонио претходни кадар
  • Попуњава платно црном бојом позадине
  • Поново црта слику хероја на новој позицији

Можете ли смислити разлог зашто поновно цртање вашег хероја много пута у секунди може довести до трошкова перформанси? Прочитајте о алтернативама овом обрасцу.

Обрада догађаја са тастатуре

Овде повезујемо унос играча са акцијом у игри. Када неко притисне тастер за пуцање ласером или стрелицу за избегавање астероида, ваша игра мора да детектује и одговори на тај унос.

Догађаји са тастатуре се дешавају на нивоу прозора, што значи да цео прозор вашег претраживача слуша те притиске тастера. Кликови мишем, с друге стране, могу бити везани за одређене елементе (као што је кликање на дугме). За нашу свемирску игру, фокусираћемо се на контроле тастатуре јер оне дају играчима тај класичан аркадни осећај.

Ово ме подсећа на то како су телеграфисти у 19. веку морали да преводе унос Морзеове азбуке у смислене поруке ми радимо нешто слично, преводимо притиске тастера у команде игре.

Да бисте обрадили догађај, потребно је да користите метод addEventListener() прозора и да му дате два улазна параметра. Први параметар је назив догађаја, на пример keyup. Други параметар је функција која треба да се позове као резултат догађаја.

Ево примера:

window.addEventListener('keyup', (evt) => {
  // evt.key = string representation of the key
  if (evt.key === 'ArrowUp') {
    // do something
  }
});

Објашњење шта се овде дешава:

  • Слуша догађаје са тастатуре на целом прозору
  • Хвата објекат догађаја који садржи информације о томе који је тастер притиснут
  • Проверава да ли притиснути тастер одговара одређеном тастеру (у овом случају, горе стрелица)
  • Извршава код када је услов испуњен

За догађаје са тастатуре постоје два својства на објекту догађаја која можете користити да бисте видели који је тастер притиснут:

  • key - ово је стринг репрезентација притиснутог тастера, на пример 'ArrowUp'
  • keyCode - ово је нумеричка репрезентација, на пример 37, што одговара ArrowLeft

Манипулација догађајима са тастатуре је корисна и ван развоја игара. Које друге примене можете замислити за ову технику?

Посебни тастери: упозорење!

Неки тастери имају уграђене функције претраживача које могу ометати вашу игру. Стрелице померају страницу, а размак скаче надоле понашања која не желите када неко покушава да управља својим свемирским бродом.

Можемо спречити ова подразумевана понашања и дозволити нашој игри да обради унос. Ово је слично томе како су рани програмери рачунара морали да прекину системске интерупције како би креирали прилагођена понашања ми то радимо на нивоу претраживача. Ево како:

const onKeyDown = function (e) {
  console.log(e.keyCode);
  switch (e.keyCode) {
    case 37:
    case 39:
    case 38:
    case 40: // Arrow keys
    case 32:
      e.preventDefault();
      break; // Space
    default:
      break; // do not block other keys
  }
};

window.addEventListener('keydown', onKeyDown);

Објашњење овог кода за спречавање:

  • Проверава одређене кодове тастера који могу изазвати нежељено понашање претраживача
  • Спречава подразумевану акцију претраживача за стрелице и размакницу
  • Дозвољава другим тастерима да нормално функционишу
  • Користи e.preventDefault() да заустави уграђено понашање претраживача

Кретање изазвано игром

Сада ћемо говорити о објектима који се крећу без уноса играча. Размислите о непријатељским бродовима који круже екраном, мецима који лете праволинијски или облацима који се крећу у позадини. Ово аутономно кретање чини ваш свет игре живим чак и када нико не додирује контроле.

Користимо уграђене JavaScript тајмере за ажурирање позиција у редовним интервалима. Овај концепт је сличан начину на који раде клатна у часовницима редовни механизам који покреће доследне, временски одређене акције. Ево како то може изгледати:

const id = setInterval(() => {
  // Move the enemy on the y axis
  enemy.y += 10;
}, 100);

Шта овај код за кретање ради:

  • Креира тајмер који се покреће сваких 100 милисекунди
  • Ажурира y-координату непријатеља за 10 пиксела сваки пут
  • Чува ID интервала како бисмо га могли зауставити касније ако је потребно
  • Помера непријатеља надоле на екрану аутоматски

Петља игре

Ево концепта који све повезује петља игре. Ако би ваша игра била филм, петља игре би била пројектор филма, приказујући кадар за кадром тако брзо да све изгледа као да се глатко креће.

Свака игра има једну од ових петљи која ради у позадини. То је функција која ажурира све објекте игре, поново црта екран и континуирано понавља овај процес. Ово прати вашег хероја, све непријатеље, било које ласере који лете около целокупно стање игре.

Овај концепт ме подсећа на то како су рани филмски аниматори попут Волта Дизнија морали да поново цртају ликове кадар по кадар како би створили илузију кретања. Ми радимо исто, само кодом уместо оловкама.

Ево како типична петља игре може изгледати у коду:

const gameLoopId = setInterval(() => {
  function gameLoop() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawHero();
    drawEnemies();
    drawStaticObjects();
  }
  gameLoop();
}, 200);

Разумевање структуре петље игре:

  • Брише цело платно како би уклонио претходни кадар
  • Попуњава позадину чврстом бојом
  • Црта све објекте игре на њиховим тренутним позицијама
  • Понавља овај процес сваких 200 милисекунди како би створио глатку анимацију
  • Управља брзином кадрова контролишући временске интервале

Наставак свемирске игре

Сада ћемо додати кретање у статичну сцену коју сте претходно направили. Претворићемо је из снимка екрана у интерактивно искуство. Проћи ћемо кроз ово корак по корак како бисмо осигурали да се сваки део надовезује на претходни.

Пребаците код са места где смо стали у претходној лекцији (или започните са кодом из Почетни код за део II фасцикле ако вам је потребан нови почетак).

Ево шта данас градимо:

  • Контроле хероја: Стрелице ће управљати вашим свемирским бродом по екрану
  • Кретање непријатеља: Ти ванземаљски бродови ће почети да напредују

Хајде да почнемо са имплементацијом ових функција.

Препоручени кораци

Пронађите датотеке које су креиране за вас у your-work подфасцикли. Требало би да садрже следеће:

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

Започните свој пројекат у your-work фасцикли тако што ћете укуцати:

cd your-work
npm start

Шта ова команда ради:

  • Прелази у директоријум вашег пројекта
  • Покреће HTTP сервер на адреси http://localhost:5000
  • Сервира ваше датотеке игре како бисте их могли тестирати у претраживачу

Горенаведено ће покренути HTTP сервер на адреси http://localhost:5000. Отворите претраживач и унесите ту адресу, тренутно би требало да се приказују херој и сви непријатељи; ништа се још не помера!

Додајте код

  1. Додајте посебне објекте за hero, enemy и game object, они би требало да имају својства x и y. (Сетите се дела о Наслеђивању или композицији).

    САВЕТ game object би требало да буде онај са x и y и способношћу да се нацрта на платну.

    Савет: Почните додавањем нове класе GameObject са њеним конструктором дефинисаним као у наставку, а затим је нацртајте на платну:

    class GameObject {
      constructor(x, y) {
        this.x = x;
        this.y = y;
        this.dead = false;
        this.type = "";
        this.width = 0;
        this.height = 0;
        this.img = undefined;
      }
    
      draw(ctx) {
        ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
      }
    }
    

    Разумевање ове основне класе:

    • Дефинише заједничка својства која деле сви објекти игре (позиција, величина, слика)
    • Укључује заставицу dead за праћење да ли објекат треба уклонити
    • Обезбеђује метод draw() који приказује објекат на платну
    • Поставља подразумеване вредности за сва својства која подкласе могу заменити

    Сада, проширите ову GameObject класу да бисте креирали Hero и Enemy:

    class Hero extends GameObject {
      constructor(x, y) {
        super(x, y);
        this.width = 98;
        this.height = 75;
        this.type = "Hero";
        this.speed = 5;
      }
    }
    
    class Enemy extends GameObject {
      constructor(x, y) {
        super(x, y);
        this.width = 98;
        this.height = 50;
        this.type = "Enemy";
        const id = setInterval(() => {
          if (this.y < canvas.height - this.height) {
            this.y += 5;
          } else {
            console.log('Stopped at', this.y);
            clearInterval(id);
          }
        }, 300);
      }
    }
    

    Кључни концепти у овим класама:

    • Наслеђује од GameObject користећи кључну реч extends
    • Позива родитељски конструктор са super(x, y)
    • Поставља специфичне димензије и својства за сваки тип објекта
    • Имплементира аутоматско кретање за непријатеље користећи setInterval()
  2. Додајте обрађиваче догађаја са тастатуре за навигацију тастерима (померање хероја горе/доле, лево/десно)

    ЗАПАМТИТЕ то је картезијски систем, горњи леви угао је 0,0. Такође запамтите да додате код за заустављање подразумеваног понашања.

    Савет: Направите своју функцију onKeyDown и повежите је са прозором:

    const onKeyDown = function (e) {
      console.log(e.keyCode);
      // Add the code from the lesson above to stop default behavior
      switch (e.keyCode) {
        case 37:
        case 39:
        case 38:
        case 40: // Arrow keys
        case 32:
          e.preventDefault();
          break; // Space
        default:
          break; // do not block other keys
      }
    };
    
    window.addEventListener("keydown", onKeyDown);
    

    Шта овај обрађивач догађаја ради:

    • Слуша догађаје притискања тастера на целом прозору
    • Бележи код тастера како би вам помогао да откријете који тастери се притискају
    • Спречава подразумевано понашање претраживача за стрелице и размакницу
    • Дозвољава другим тастерима да нормално функционишу

    Проверите конзолу вашег претраживача у овом тренутку и посматрајте притиске тастера који се бележе.

  3. Имплементирајте Pub sub pattern, ово ће одржати ваш код чистим док настављате са преосталим деловима.

    Образац објављивања-претплате помаже у организовању вашег кода раздвајањем детекције догађаја од обраде догађаја. Ово чини ваш код модуларнијим и лакшим за одржавање.

    Да бисте урадили овај последњи део, можете:

    1. Додајте слушач догађаја на прозор:

      window.addEventListener("keyup", (evt) => {
        if (evt.key === "ArrowUp") {
          eventEmitter.emit(Messages.KEY_EVENT_UP);
        } else if (evt.key === "ArrowDown") {
          eventEmitter.emit(Messages.KEY_EVENT_DOWN);
        } else if (evt.key === "ArrowLeft") {
          eventEmitter.emit(Messages.KEY_EVENT_LEFT);
        } else if (evt.key === "ArrowRight") {
          eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
        }
      });
      

    Шта овај систем догађаја ради:

    • Детектује унос са тастатуре и претвара га у прилагођене догађаје игре
    • Одваја детекцију уноса од лог
  • Креира мрежу непријатеља користећи угнеждене петље
  • Додељује слику непријатеља сваком објекту непријатеља
  • Додаје сваког непријатеља у глобални низ објеката игре

и додајте функцију createHero() да уради сличан процес за хероја.

```javascript
function createHero() {
  hero = new Hero(
    canvas.width / 2 - 45,
    canvas.height - canvas.height / 4
  );
  hero.img = heroImg;
  gameObjects.push(hero);
}
```

Шта ради креирање хероја:

  • Поставља хероја на дно центра екрана
  • Додељује слику хероја објекту хероја
  • Додаје хероја у низ објеката игре ради рендеровања

и на крају, додајте функцију drawGameObjects() да започнете цртање:

```javascript
function drawGameObjects(ctx) {
  gameObjects.forEach(go => go.draw(ctx));
}
```

Разумевање функције за цртање:

  • Итерира кроз све објекте игре у низу
  • Позива метод draw() за сваки објекат
  • Прослеђује контекст платна како би објекти могли сами себе да рендерују

Ваши непријатељи би требало да почну да напредују ка вашем свемирском броду хероја!
}
}
```

and add a `createHero()` function to do a similar process for the hero.

```javascript
function createHero() {
  hero = new Hero(
    canvas.width / 2 - 45,
    canvas.height - canvas.height / 4
  );
  hero.img = heroImg;
  gameObjects.push(hero);
}
```

и на крају, додајте функцију drawGameObjects() да започнете цртање:

```javascript
function drawGameObjects(ctx) {
  gameObjects.forEach(go => go.draw(ctx));
}
```

Ваши непријатељи би требало да почну да напредују ка вашем свемирском броду хероја!


Изазов GitHub Copilot Agent 🚀

Ево изазова који ће побољшати изглед ваше игре: додавање граница и глатких контрола. Тренутно ваш херој може да одлети са екрана, а кретање може деловати трзаво.

Ваш задатак: Учините да ваш свемирски брод делује реалистичније имплементирањем граница екрана и флуидног кретања. Ово је слично начину на који НАСА-ини системи контроле лета спречавају свемирске летелице да пређу безбедне оперативне параметре.

Шта треба да направите: Креирајте систем који држи свемирски брод хероја на екрану и учините да контроле буду глатке. Када играчи држе притиснут тастер са стрелицом, брод би требало да клизи континуирано, а не да се креће у дискретним корацима. Размислите о додавању визуелног ефекта када брод достигне границе екрана можда суптилан ефекат који указује на ивицу зоне игре.

Сазнајте више о режиму агента овде.

🚀 Изазов

Организација кода постаје све важнија како пројекти расту. Можда сте приметили да ваш фајл постаје претрпан функцијама, променљивама и класама које су све измешане. Ово ме подсећа на то како су инжењери који су организовали код за Аполо мисију морали да креирају јасне, одрживе системе на којима су могли да раде различити тимови истовремено.

Ваш задатак:
Размишљајте као софтверски архитекта. Како бисте организовали свој код тако да шест месеци од сада, ви (или ваш тимски колега) можете разумети шта се дешава? Чак и ако све остане у једном фајлу за сада, можете креирати бољу организацију:

  • Груписање повезаних функција заједно са јасним коментарима
  • Одвајање задатака - одвојите логику игре од рендеровања
  • Коришћење конзистентних назива за променљиве и функције
  • Креирање модула или простора имена за организовање различитих аспеката ваше игре
  • Додавање документације која објашњава сврху сваког главног дела

Питања за размишљање:

  • Који делови вашег кода су најтежи за разумевање када се вратите на њих?
  • Како бисте могли да организујете свој код да би било лакше за неког другог да допринесе?
  • Шта би се десило ако бисте желели да додате нове функције као што су појачања или различите типове непријатеља?

Квиз након предавања

Квиз након предавања

Преглед и самостално учење

Све градимо од нуле, што је фантастично за учење, али ево једне мале тајне постоје невероватни JavaScript оквири који могу обавити велики део посла уместо вас. Када се осећате удобно са основама које смо покрили, вреди истражити шта је доступно.

Размислите о оквирима као о добро опремљеном алату уместо да правите сваки алат ручно. Они могу решити многе изазове организације кода о којима смо говорили, плус понудити функције које би вам одузеле недеље да их сами направите.

Ствари које вреди истражити:

  • Како моторе за игре организују код бићете задивљени паметним обрасцима које користе
  • Трикови за перформансе који чине да игре на платну раде изузетно глатко
  • Модерне JavaScript функције које могу учинити ваш код чистијим и лакшим за одржавање
  • Различити приступи управљању објектима игре и њиховим односима

Задатак

Коментаришите свој код


Одрицање од одговорности:
Овај документ је преведен помоћу услуге за превођење вештачке интелигенције Co-op Translator. Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не преузимамо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.