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
Lee Stott 2daab5271b
Update Quiz Link
3 weeks ago
..
README.md Update Quiz Link 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

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

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

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

Игре нису баш забавне док немате ванземаљце који се крећу по екрану! У овој игри ћемо користити две врсте кретања:

  • Кретање помоћу тастатуре/мишa: када корисник интерагује са тастатуром или мишем како би померио објекат на екрану.
  • Кретање изазвано игром: када игра помера објекат у одређеним временским интервалима.

Како онда померамо ствари на екрану? Све се своди на координатни систем: мењамо локацију (x, y) објекта и затим поново цртамо екран.

Типично, потребни су следећи кораци да би се постигло кретање на екрану:

  1. Поставите нову локацију за објекат; ово је неопходно како би се објекат перципирао као да се померио.
  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);

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

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

Догађаје обрађујете тако што повезујете одређене догађаје са кодом. Догађаји тастатуре се активирају на целом прозору, док се догађаји миша, попут click, могу повезати са кликом на одређени елемент. Кроз овај пројекат користићемо догађаје тастатуре.

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

Ево примера:

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

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

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

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

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

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

let 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().

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

Можемо учинити да се ствари саме крећу користећи тајмере као што су функције setTimeout() или setInterval() које ажурирају локацију објекта на сваком откуцају или временском интервалу. Ево како то може изгледати:

let id = setInterval(() => {
  //move the enemy on the y axis
  enemy.y += 10;
})

Петља игре

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

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

let 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();
}, 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. Отворите прегледач и унесите ту адресу, тренутно би требало да се прикаже херој и сви непријатељи; ништа се још не креће!

Додајте код

  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);
      }
    }
    

    Сада проширите овај GameObject како бисте креирали Hero и Enemy.

    class Hero extends GameObject {
      constructor(x, y) {
        ...it needs an x, y, type, and speed
      }
    }
    
    class Enemy extends GameObject {
      constructor(x, y) {
        super(x, y);
        (this.width = 98), (this.height = 50);
        this.type = "Enemy";
        let id = setInterval(() => {
          if (this.y < canvas.height - this.height) {
            this.y += 5;
          } else {
            console.log('Stopped at', this.y)
            clearInterval(id);
          }
        }, 300)
      }
    }
    
  2. Додајте обрађиваче догађаја тастера за управљање навигацијом тастера (померање хероја горе/доле, лево/десно).

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

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

     let onKeyDown = function (e) {
           console.log(e.keyCode);
             ...add the code from the lesson above to stop default behavior
           }
     };
    
     window.addEventListener("keydown", onKeyDown);
    

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

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

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

    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);
         }
       });
      
    2. Креирајте класу EventEmitter за објављивање и претплату на поруке:

      class EventEmitter {
        constructor() {
          this.listeners = {};
        }
      
        on(message, listener) {
          if (!this.listeners[message]) {
            this.listeners[message] = [];
          }
          this.listeners[message].push(listener);
        }
      
        emit(message, payload = null) {
          if (this.listeners[message]) {
            this.listeners[message].forEach((l) => l(message, payload));
          }
        }
      }
      
    3. Додајте константе и подесите EventEmitter:

      const Messages = {
        KEY_EVENT_UP: "KEY_EVENT_UP",
        KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
        KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
        KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
      };
      
      let heroImg, 
          enemyImg, 
          laserImg,
          canvas, ctx, 
          gameObjects = [], 
          hero, 
          eventEmitter = new EventEmitter();
      
    4. Иницијализујте игру

    function initGame() {
      gameObjects = [];
      createEnemies();
      createHero();
    
      eventEmitter.on(Messages.KEY_EVENT_UP, () => {
        hero.y -=5 ;
      })
    
      eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
        hero.y += 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
        hero.x -= 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
        hero.x += 5;
      });
    }
    
  4. Подесите петљу игре

    Префакторишите функцију window.onload како бисте иницијализовали игру и подесили петљу игре у добром интервалу. Такође ћете додати ласерски зрак:

    window.onload = async () => {
      canvas = document.getElementById("canvas");
      ctx = canvas.getContext("2d");
      heroImg = await loadTexture("assets/player.png");
      enemyImg = await loadTexture("assets/enemyShip.png");
      laserImg = await loadTexture("assets/laserRed.png");
    
      initGame();
      let gameLoopId = setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        drawGameObjects(ctx);
      }, 100)
    
    };
    
  5. Додајте код за померање непријатеља у одређеним интервалима

    Префакторишите функцију createEnemies() како бисте креирали непријатеље и додали их у нову класу gameObjects:

    function createEnemies() {
      const MONSTER_TOTAL = 5;
      const MONSTER_WIDTH = MONSTER_TOTAL * 98;
      const START_X = (canvas.width - MONSTER_WIDTH) / 2;
      const STOP_X = START_X + MONSTER_WIDTH;
    
      for (let x = START_X; x < STOP_X; x += 98) {
        for (let y = 0; y < 50 * 5; y += 50) {
          const enemy = new Enemy(x, y);
          enemy.img = enemyImg;
          gameObjects.push(enemy);
        }
      }
    }
    

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

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

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

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

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


🚀 Изазов

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

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

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

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

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

Задатак

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


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