āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
2015 āĻ¸āĻžāĻ˛ā§‡ āĻŽā§āĻ•ā§āĻ¤āĻŋ āĻĒāĻžāĻ¯āĻŧ Agar.io āĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻ§āĻžāĻ°āĻžāĻ° āĻĒā§‚āĻ°ā§āĻŦāĻĒā§āĻ°ā§āĻˇ āĻšāĻ¯āĻŧā§‡ āĻ‰āĻ ā§‡āĻ›ā§‡ āĻ—ā§‡āĻŽāĻ¸ .ioāĻ¯āĻž āĻ¤āĻ–āĻ¨ āĻĨā§‡āĻ•ā§‡ āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧāĻ¤āĻž āĻŦā§ƒāĻĻā§āĻ§āĻŋ āĻĒā§‡āĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤ āĻ†āĻŽāĻŋ āĻŦā§āĻ¯āĻ•ā§āĻ¤āĻŋāĻ—āĻ¤āĻ­āĻžāĻŦā§‡ .io āĻ—ā§‡āĻŽā§‡āĻ° āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧāĻ¤āĻž āĻŦā§ƒāĻĻā§āĻ§āĻŋāĻ° āĻ…āĻ­āĻŋāĻœā§āĻžāĻ¤āĻž āĻĒā§‡āĻ¯āĻŧā§‡āĻ›āĻŋ: āĻ—āĻ¤ āĻ¤āĻŋāĻ¨ āĻŦāĻ›āĻ°ā§‡, āĻ†āĻŽāĻŋ āĻ•āĻ°ā§‡āĻ›āĻŋ āĻāĻ‡ āĻ§āĻžāĻ°āĻžāĻ° āĻĻā§āĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻāĻŦāĻ‚ āĻŦāĻŋāĻ•ā§āĻ°āĻŋ āĻ•āĻ°ā§‡āĻ›ā§‡āĨ¤.

āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻāĻ‡ āĻ—ā§‡āĻŽāĻ—ā§āĻ˛āĻŋāĻ° āĻ•āĻĨāĻž āĻ†āĻ—ā§‡ āĻ•āĻ–āĻ¨āĻ“ āĻļā§‹āĻ¨ā§‡āĻ¨ āĻ¨āĻŋ, āĻ¤āĻŦā§‡ āĻāĻ‡āĻ—ā§āĻ˛āĻŋ āĻŦāĻŋāĻ¨āĻžāĻŽā§‚āĻ˛ā§āĻ¯ā§‡ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¯āĻž āĻ–ā§‡āĻ˛āĻž āĻ¸āĻšāĻœ (āĻ•ā§‹āĻ¨ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ āĻ¨ā§‡āĻ‡)āĨ¤ āĻ¤āĻžāĻ°āĻž āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻāĻ•āĻ‡ āĻ…āĻ™ā§āĻ—āĻ¨ā§‡ āĻ…āĻ¨ā§‡āĻ• āĻŦāĻŋāĻ°ā§‹āĻ§ā§€ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧā§‡āĻ° āĻŽā§āĻ–ā§‹āĻŽā§āĻ–āĻŋ āĻšāĻ¯āĻŧāĨ¤ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻŦāĻŋāĻ–ā§āĻ¯āĻžāĻ¤ .io āĻ—ā§‡āĻŽ: Slither.io и āĻĄāĻžāĻ¯āĻŧāĻŋāĻĒ.āĻ‡āĻ“.

āĻāĻ‡ āĻĒā§‹āĻ¸ā§āĻŸā§‡, āĻ†āĻŽāĻ°āĻž āĻ•āĻŋāĻ­āĻžāĻŦā§‡ āĻ…āĻ¨ā§āĻŦā§‡āĻˇāĻŖ āĻ•āĻ°āĻŦ āĻ¸ā§āĻ•ā§āĻ°ā§āĻ¯āĻžāĻš āĻĨā§‡āĻ•ā§‡ āĻāĻ•āĻŸāĻŋ .io āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨. āĻāĻ‡ āĻœāĻ¨ā§āĻ¯, āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻœāĻžāĻ­āĻžāĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ āĻœā§āĻžāĻžāĻ¨ āĻ¯āĻĨā§‡āĻˇā§āĻŸ āĻšāĻŦā§‡: āĻ†āĻĒāĻ¨āĻŋ āĻ¸āĻŋāĻ¨āĻŸā§āĻ¯āĻžāĻ•ā§āĻ¸ āĻŽāĻ¤ āĻœāĻŋāĻ¨āĻŋāĻ¸ āĻŦā§āĻāĻ¤ā§‡ āĻšāĻŦā§‡ ES6, āĻ•ā§€āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ this и āĻ…āĻ™ā§āĻ—āĻŋāĻ•āĻžāĻ°. āĻœāĻžāĻ­āĻžāĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻœā§āĻžāĻžāĻ¨ āĻ¨āĻŋāĻ–ā§āĻāĻ¤ āĻ¨āĻž āĻšāĻ˛ā§‡āĻ“, āĻ†āĻĒāĻ¨āĻŋ āĻāĻ–āĻ¨āĻ“ āĻŦā§‡āĻļāĻŋāĻ°āĻ­āĻžāĻ— āĻĒā§‹āĻ¸ā§āĻŸ āĻŦā§āĻāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤

io āĻ—ā§‡āĻŽā§‡āĻ° āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ

āĻļā§‡āĻ–āĻžāĻ° āĻ¸āĻšāĻžāĻ¯āĻŧāĻ¤āĻžāĻ° āĻœāĻ¨ā§āĻ¯, āĻ†āĻŽāĻ°āĻž āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ– āĻ•āĻ°āĻŦ io āĻ—ā§‡āĻŽā§‡āĻ° āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ. āĻāĻŸāĻž āĻ–ā§‡āĻ˛āĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§āĻ¨!

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻ—ā§‡āĻŽāĻŸāĻŋ āĻŦā§‡āĻļ āĻ¸āĻšāĻœ: āĻ†āĻĒāĻ¨āĻŋ āĻāĻŽāĻ¨ āĻāĻ•āĻŸāĻŋ āĻ…āĻ™ā§āĻ—āĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻœāĻžāĻšāĻžāĻœ āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ•āĻ°ā§‡āĻ¨ āĻ¯ā§‡āĻ–āĻžāĻ¨ā§‡ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻžāĻ° āĻœāĻžāĻšāĻžāĻœ āĻ¸ā§āĻŦāĻ¯āĻŧāĻ‚āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻ­āĻžāĻŦā§‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻĢāĻžāĻ¯āĻŧāĻžāĻ° āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻŋ āĻ¤āĻžāĻĻā§‡āĻ° āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻāĻĄāĻŧāĻŋāĻ¯āĻŧā§‡ āĻ…āĻ¨ā§āĻ¯ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ†āĻ˜āĻžāĻ¤ āĻ•āĻ°āĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§‡āĻ¨āĨ¤

1. āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒā§‡āĻ° āĻ¸āĻ‚āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤ āĻŦāĻŋāĻŦāĻ°āĻŖ / āĻ•āĻžāĻ āĻžāĻŽā§‹

āĻ†āĻŽāĻŋ āĻ¸ā§āĻĒāĻžāĻ°āĻŋāĻļ āĻ•āĻ°āĻ›āĻŋ āĻ¸ā§‹āĻ°ā§āĻ¸ āĻ•ā§‹āĻĄ āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ āĻ•āĻ°ā§āĻ¨ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ āĻ—ā§‡āĻŽ āĻ¯āĻžāĻ¤ā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻŽāĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤

āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡:

  • āĻĒā§āĻ°āĻ•āĻžāĻļ āĻ•āĻ°āĻž āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ Node.js āĻ“āĻ¯āĻŧā§‡āĻŦ āĻĢā§āĻ°ā§‡āĻŽāĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻ• āĻ¯āĻž āĻ—ā§‡āĻŽā§‡āĻ° āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§‡āĨ¤
  • socket.io - āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻāĻŦāĻ‚ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻĄā§‡āĻŸāĻž āĻ†āĻĻāĻžāĻ¨-āĻĒā§āĻ°āĻĻāĻžāĻ¨ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻ•āĻŸāĻŋ āĻ“āĻ¯āĻŧā§‡āĻŦāĻ¸āĻ•ā§‡āĻŸ āĻ˛āĻžāĻ‡āĻŦā§āĻ°ā§‡āĻ°āĻŋāĨ¤
  • webpack - āĻŽāĻĄāĻŋāĻ‰āĻ˛ āĻŽā§āĻ¯āĻžāĻ¨ā§‡āĻœāĻžāĻ°āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ•ā§‡āĻ¨ Webpack āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¸ā§‡ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻĒāĻĄāĻŧāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤ āĻāĻ–āĻžāĻ¨ā§‡.

āĻāĻ–āĻžāĻ¨ā§‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸ āĻĄāĻŋāĻ°ā§‡āĻ•ā§āĻŸāĻ°āĻŋ āĻ—āĻ āĻ¨ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻ•ā§‡āĻŽāĻ¨:

public/
    assets/
        ...
src/
    client/
        css/
            ...
        html/
            index.html
        index.js
        ...
    server/
        server.js
        ...
    shared/
        constants.js

āĻ¸āĻ°ā§āĻŦāĻœāĻ¨ā§€āĻ¨/

āĻāĻ•āĻŸāĻŋ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡ āĻ¸āĻŦāĻ•āĻŋāĻ›ā§ public/ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¸ā§āĻĨāĻŋāĻ°āĻ­āĻžāĻŦā§‡ āĻœāĻŽāĻž āĻĻā§‡āĻ“āĻ¯āĻŧāĻž āĻšāĻŦā§‡āĨ¤ āĻ­āĻŋāĻ¤āĻ°ā§‡ public/assets/ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒ āĻĻā§āĻŦāĻžāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ āĻ›āĻŦāĻŋ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤

src /

āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¸ā§‹āĻ°ā§āĻ¸ āĻ•ā§‹āĻĄ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡ āĻ†āĻ›ā§‡ src/. āĻ¨āĻžāĻŽ client/ и server/ āĻ¨āĻŋāĻœā§‡āĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ•āĻĨāĻž āĻŦāĻ˛ā§āĻ¨ āĻāĻŦāĻ‚ shared/ āĻāĻ•āĻŸāĻŋ āĻ§ā§āĻ°ā§āĻŦāĻ• āĻĢāĻžāĻ‡āĻ˛ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡ āĻ¯āĻž āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻāĻŦāĻ‚ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ‰āĻ­āĻ¯āĻŧ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĨ¤

2. āĻ¸āĻŽāĻžāĻŦā§‡āĻļ/āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸

āĻ‰āĻĒāĻ°ā§‡ āĻ‰āĻ˛ā§āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻšāĻŋāĻ¸āĻžāĻŦā§‡, āĻ†āĻŽāĻ°āĻž āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻ¤ā§‡ āĻŽāĻĄāĻŋāĻ‰āĻ˛ āĻŽā§āĻ¯āĻžāĻ¨ā§‡āĻœāĻžāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋāĨ¤ webpack. āĻ†āĻ¸ā§āĻ¨ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ• āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨āĻŸāĻŋ āĻāĻ•āĻŦāĻžāĻ° āĻĻā§‡āĻ–ā§‡ āĻ¨ā§‡āĻ“āĻ¯āĻŧāĻž āĻ¯āĻžāĻ•:

webpack.common.js:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: {
    game: './src/client/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/client/html/index.html',
    }),
  ],
};

āĻāĻ–āĻžāĻ¨ā§‡ āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ˛āĻžāĻ‡āĻ¨ āĻšāĻ˛:

  • src/client/index.js āĻœāĻžāĻ­āĻžāĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ (JS) āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸāĨ¤ āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ• āĻāĻ–āĻžāĻ¨ āĻĨā§‡āĻ•ā§‡ āĻļā§āĻ°ā§ āĻšāĻŦā§‡ āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°āĻž āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻŦāĻžāĻ°āĻŦāĻžāĻ° āĻ…āĻ¨ā§āĻ¸āĻ¨ā§āĻ§āĻžāĻ¨ āĻ•āĻ°āĻŦā§‡āĨ¤
  • āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ• āĻŦāĻŋāĻ˛ā§āĻĄā§‡āĻ° āĻ†āĻ‰āĻŸāĻĒā§āĻŸ JS āĻĄāĻŋāĻ°ā§‡āĻ•ā§āĻŸāĻ°āĻŋāĻ¤ā§‡ āĻ…āĻŦāĻ¸ā§āĻĨāĻŋāĻ¤ āĻšāĻŦā§‡ dist/. āĻ†āĻŽāĻŋ āĻāĻ‡ āĻĢāĻžāĻ‡āĻ˛āĻŸāĻŋāĻ•ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•āĻ˛ āĻ•āĻ°āĻŦ js āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ.
  • āĻ†āĻŽāĻ°āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋ āĻšāĻŸā§āĻŸāĻ—ā§‹āĻ˛, āĻāĻŦāĻ‚ āĻŦāĻŋāĻļā§‡āĻˇ āĻ•āĻ°ā§‡ āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨ @babel/preset-env āĻĒā§āĻ°āĻžāĻ¨ā§‹ āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ°āĻ—ā§āĻ˛āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻœā§‡āĻāĻ¸ āĻ•ā§‹āĻĄ āĻŸā§āĻ°āĻžāĻ¨ā§āĻ¸āĻĒāĻŋāĻ˛āĻŋāĻ‚ āĻ•āĻ°āĻ¤ā§‡āĨ¤
  • āĻ†āĻŽāĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ›āĻŋ JS āĻĢāĻžāĻ‡āĻ˛ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ°ā§‡āĻĢāĻžāĻ°ā§‡āĻ¨ā§āĻ¸āĻ•ā§ƒāĻ¤ āĻ¸āĻŽāĻ¸ā§āĻ¤ CSS āĻŦā§‡āĻ° āĻ•āĻ°āĻ¤ā§‡ āĻāĻŦāĻ‚ āĻ¸ā§‡āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻāĻ• āĻœāĻžāĻ¯āĻŧāĻ—āĻžāĻ¯āĻŧ āĻāĻ•āĻ¤ā§āĻ°āĻŋāĻ¤ āĻ•āĻ°āĻ¤ā§‡āĨ¤ āĻ†āĻŽāĻŋ āĻ¤āĻžāĻ•ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻĄāĻžāĻ•āĻŦ css āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ.

āĻ†āĻĒāĻ¨āĻŋ āĻ…āĻĻā§āĻ­ā§āĻ¤ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽ āĻ˛āĻ•ā§āĻˇā§āĻ¯ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨ '[name].[contenthash].ext'. āĻ¤āĻžāĻ°āĻž āĻ§āĻžāĻ°āĻŖ āĻ•āĻ°ā§‡ āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽ āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ•: [name] āĻ‡āĻ¨āĻĒā§āĻŸ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻ¨āĻžāĻŽā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻŋāĻ¤ āĻšāĻŦā§‡ (āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°ā§‡, āĻāĻŸāĻŋ game), āĻāĻŦāĻ‚ [contenthash] āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§āĻ° āĻāĻ•āĻŸāĻŋ āĻšā§āĻ¯āĻžāĻļ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻŋāĻ¤ āĻšāĻŦā§‡āĨ¤ āĻ†āĻŽāĻ°āĻž āĻāĻŸāĻž āĻ•āĻ°āĻ¤ā§‡ āĻšā§āĻ¯āĻžāĻļāĻŋāĻ‚āĻ¯āĻŧā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒāĻŸāĻŋ āĻ…āĻĒā§āĻŸāĻŋāĻŽāĻžāĻ‡āĻœ āĻ•āĻ°ā§āĻ¨ - āĻ†āĻĒāĻ¨āĻŋ āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ°āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻœā§‡āĻāĻ¸ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ…āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸāĻ•āĻžāĻ˛ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ•ā§āĻ¯āĻžāĻļā§‡ āĻ•āĻ°āĻ¤ā§‡ āĻŦāĻ˛āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨, āĻ•āĻžāĻ°āĻŖ āĻ¯āĻĻāĻŋ āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻšāĻ¯āĻŧ, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻ¤āĻžāĻ° āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽāĻ“ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻšāĻ¯āĻŧ (āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ contenthash) āĻšā§‚āĻĄāĻŧāĻžāĻ¨ā§āĻ¤ āĻĢāĻ˛āĻžāĻĢāĻ˛ āĻ­āĻŋāĻ‰ āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽ āĻšāĻŦā§‡ game.dbeee76e91a97d0c7207.js.

āĻĢāĻžāĻ‡āĻ˛ webpack.common.js āĻŦā§‡āĻ¸ āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨ āĻĢāĻžāĻ‡āĻ˛ āĻ¯āĻž āĻ†āĻŽāĻ°āĻž āĻĄā§‡āĻ­ā§‡āĻ˛āĻĒāĻŽā§‡āĻ¨ā§āĻŸ āĻāĻŦāĻ‚ āĻ¸āĻŽāĻžāĻĒā§āĻ¤ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸ āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨ā§‡ āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°āĻŋāĨ¤ āĻāĻ–āĻžāĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ āĻ‰āĻ¨ā§āĻ¨āĻ¯āĻŧāĻ¨ āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨:

webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
});

āĻĻāĻ•ā§āĻˇāĻ¤āĻžāĻ° āĻœāĻ¨ā§āĻ¯, āĻ†āĻŽāĻ°āĻž āĻ‰āĻ¨ā§āĻ¨āĻ¯āĻŧāĻ¨ āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻžāĻ¯āĻŧ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋ webpack.dev.js, āĻāĻŦāĻ‚ āĻ¸ā§āĻ‡āĻš āĻ•āĻ°ā§‡ webpack.prod.jsāĻ‰ā§ŽāĻĒāĻžāĻĻāĻ¨ā§‡ āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœā§‡āĻ° āĻ†āĻ•āĻžāĻ° āĻ…āĻĒā§āĻŸāĻŋāĻŽāĻžāĻ‡āĻœ āĻ•āĻ°āĻ¤ā§‡āĨ¤

āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ āĻ¸ā§‡āĻŸāĻŋāĻ‚

āĻ†āĻŽāĻŋ āĻāĻ•āĻŸāĻŋ āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ āĻŽā§‡āĻļāĻŋāĻ¨ā§‡ āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒāĻŸāĻŋ āĻ‡āĻ¨āĻ¸ā§āĻŸāĻ˛ āĻ•āĻ°āĻžāĻ° āĻĒāĻ°āĻžāĻŽāĻ°ā§āĻļ āĻĻāĻŋāĻ‡ āĻ¯āĻžāĻ¤ā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻāĻ‡ āĻĒā§‹āĻ¸ā§āĻŸā§‡ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻžāĻ­ā§āĻ•ā§āĻ¤ āĻĒāĻĻāĻ•ā§āĻˇā§‡āĻĒāĻ—ā§āĻ˛āĻŋ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤ āĻ¸ā§‡āĻŸāĻ†āĻĒ āĻ¸āĻšāĻœ: āĻĒā§āĻ°āĻĨāĻŽ, āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻ‡āĻ¨āĻ¸ā§āĻŸāĻ˛ āĻ•āĻ°āĻž āĻ†āĻŦāĻļā§āĻ¯āĻ• āĻ¨ā§‹āĻĄ и NPM. āĻĒāĻ°āĻŦāĻ°ā§āĻ¤ā§€ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻŦā§‡

$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm install

āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡āĻ¤ā§‡ āĻĒā§āĻ°āĻ¸ā§āĻ¤ā§āĻ¤! āĻĄā§‡āĻ­ā§‡āĻ˛āĻĒāĻŽā§‡āĻ¨ā§āĻŸ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻļā§āĻ°ā§ āĻ•āĻ°āĻ¤ā§‡, āĻļā§āĻ§ā§ āĻšāĻžāĻ˛āĻžāĻ¨

$ npm run develop

āĻāĻŦāĻ‚ āĻ“āĻ¯āĻŧā§‡āĻŦ āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ°ā§‡ āĻ¯āĻžāĻ¨ āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ āĻšā§‹āĻ¸ā§āĻŸ: 3000. āĻ•ā§‹āĻĄ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻšāĻ“āĻ¯āĻŧāĻžāĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻžāĻĨā§‡ āĻĄā§‡āĻ­ā§‡āĻ˛āĻĒāĻŽā§‡āĻ¨ā§āĻŸ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ¸ā§āĻŦāĻ¯āĻŧāĻ‚āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻ­āĻžāĻŦā§‡ JS āĻāĻŦāĻ‚ CSS āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻĒā§āĻ¨āĻ°ā§āĻ¨āĻŋāĻ°ā§āĻŽāĻžāĻŖ āĻ•āĻ°āĻŦā§‡ - āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻ°āĻŋāĻĢā§āĻ°ā§‡āĻļ āĻ•āĻ°ā§āĻ¨!

3. āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ

āĻ†āĻ¸ā§āĻ¨ āĻ—ā§‡āĻŽ āĻ•ā§‹āĻĄ āĻ¨āĻŋāĻœā§‡āĻ‡ āĻ¨āĻžāĻŽā§āĻ¨. āĻĒā§āĻ°āĻĨāĻŽā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻĒā§ƒāĻˇā§āĻ āĻž āĻĻāĻ°āĻ•āĻžāĻ° index.html, āĻ¸āĻžāĻ‡āĻŸāĻŸāĻŋ āĻĻā§‡āĻ–āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ, āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻĒā§āĻ°āĻĨāĻŽā§‡ āĻāĻŸāĻŋ āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻŦā§‡āĨ¤ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻŦā§‡āĻļ āĻ¸āĻšāĻœ āĻšāĻŦā§‡:

index.html

āĻāĻ•āĻŸāĻŋ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ .io āĻ—ā§‡āĻŽ  āĻ–ā§‡āĻ˛āĻž

āĻāĻ‡ āĻ•ā§‹āĻĄ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻŸāĻŋ āĻ¸ā§āĻŦāĻšā§āĻ›āĻ¤āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸āĻžāĻŽāĻžāĻ¨ā§āĻ¯ āĻ¸āĻ°āĻ˛ā§€āĻ•ā§ƒāĻ¤ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡, āĻāĻŦāĻ‚ āĻ†āĻŽāĻŋ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻĒā§‹āĻ¸ā§āĻŸ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻ—ā§āĻ˛āĻŋāĻ° āĻ¸āĻžāĻĨā§‡ āĻāĻ•āĻ‡ āĻ•āĻžāĻœ āĻ•āĻ°āĻŦāĨ¤ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•ā§‹āĻĄ āĻ¸āĻ°ā§āĻŦāĻĻāĻž āĻĻā§‡āĻ–āĻž āĻ¯āĻžāĻŦā§‡ āĻ—āĻŋāĻŸāĻšāĻžāĻŦ.

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ†āĻ›ā§‡:

  • HTML5 āĻ•ā§āĻ¯āĻžāĻ¨āĻ­āĻžāĻ¸ āĻ‰āĻĒāĻžāĻĻāĻžāĻ¨ (<canvas>) āĻ¯āĻž āĻ†āĻŽāĻ°āĻž āĻ—ā§‡āĻŽāĻŸāĻŋ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦāĨ¤
  • <link> āĻ†āĻŽāĻžāĻĻā§‡āĻ° CSS āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡āĨ¤
  • <script> āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻœāĻžāĻ­āĻžāĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡.
  • āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻ¸āĻš āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻŽā§‡āĻ¨ā§ <input> āĻāĻŦāĻ‚ āĻĒā§āĻ˛ā§‡ āĻŦā§‹āĻ¤āĻžāĻŽ (<button>).

āĻšā§‹āĻŽ āĻĒā§‡āĻœ āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻĒāĻ°ā§‡, āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻœāĻžāĻ­āĻžāĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ āĻ•ā§‹āĻĄ āĻ•āĻžāĻ°ā§āĻ¯āĻ•āĻ° āĻ•āĻ°āĻž āĻļā§āĻ°ā§ āĻ•āĻ°āĻŦā§‡, āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ JS āĻĢāĻžāĻ‡āĻ˛ āĻĨā§‡āĻ•ā§‡ āĻļā§āĻ°ā§ āĻ•āĻ°ā§‡: src/client/index.js.

index.js

import { connect, play } from './networking';
import { startRendering, stopRendering } from './render';
import { startCapturingInput, stopCapturingInput } from './input';
import { downloadAssets } from './assets';
import { initState } from './state';
import { setLeaderboardHidden } from './leaderboard';

import './css/main.css';

const playMenu = document.getElementById('play-menu');
const playButton = document.getElementById('play-button');
const usernameInput = document.getElementById('username-input');

Promise.all([
  connect(),
  downloadAssets(),
]).then(() => {
  playMenu.classList.remove('hidden');
  usernameInput.focus();
  playButton.onclick = () => {
    // Play!
    play(usernameInput.value);
    playMenu.classList.add('hidden');
    initState();
    startCapturingInput();
    startRendering();
    setLeaderboardHidden(false);
  };
});

āĻāĻŸāĻŋ āĻœāĻŸāĻŋāĻ˛ āĻļā§‹āĻ¨āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡, āĻ¤āĻŦā§‡ āĻāĻ–āĻžāĻ¨ā§‡ āĻ–ā§āĻŦ āĻŦā§‡āĻļāĻŋ āĻ•āĻŋāĻ›ā§ āĻšāĻšā§āĻ›ā§‡ āĻ¨āĻž:

  1. āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻŦā§‡āĻļ āĻ•āĻ¯āĻŧā§‡āĻ•āĻŸāĻŋ JS āĻĢāĻžāĻ‡āĻ˛ āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡āĨ¤
  2. āĻ¸āĻŋāĻāĻ¸āĻāĻ¸ āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ (āĻ¤āĻžāĻ‡ āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ• āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ¸āĻŋāĻāĻ¸āĻāĻ¸ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœā§‡ āĻ¤āĻžāĻĻā§‡āĻ° āĻ…āĻ¨ā§āĻ¤āĻ°ā§āĻ­ā§āĻ•ā§āĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻœāĻžāĻ¨ā§‡)āĨ¤
  3. āĻ†āĻ°āĻŽā§āĻ­ connect() āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻāĻŦāĻ‚ āĻšāĻžāĻ˛āĻžāĻ¤ā§‡ downloadAssets() āĻ—ā§‡āĻŽāĻŸāĻŋ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§€āĻ¯āĻŧ āĻ›āĻŦāĻŋ āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻ¤ā§‡āĨ¤
  4. āĻĒāĻ°ā§āĻ¯āĻžāĻ¯āĻŧ 3 āĻ¸āĻŽāĻžāĻĒā§āĻ¤āĻŋāĻ° āĻĒāĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻŽā§‡āĻ¨ā§ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ āĻšāĻ¯āĻŧ (playMenu).
  5. "PLAY" āĻŦā§‹āĻ¤āĻžāĻŽ āĻŸāĻŋāĻĒāĻžāĻ¨ā§‹āĻ° āĻœāĻ¨ā§āĻ¯ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ° āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡ā§ˇ āĻŦā§‹āĻ¤āĻžāĻŽ āĻŸāĻŋāĻĒāĻ˛ā§‡, āĻ•ā§‹āĻĄāĻŸāĻŋ āĻ—ā§‡āĻŽāĻŸāĻŋ āĻļā§āĻ°ā§ āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°āĻ•ā§‡ āĻŦāĻ˛ā§‡ āĻ¯ā§‡ āĻ†āĻŽāĻ°āĻž āĻ–ā§‡āĻ˛āĻ¤ā§‡ āĻĒā§āĻ°āĻ¸ā§āĻ¤ā§āĻ¤āĨ¤

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ-āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ˛āĻœāĻŋāĻ•ā§‡āĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ "āĻŽāĻžāĻ‚āĻ¸" āĻ¸ā§‡āĻ‡ āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋāĻ¤ā§‡ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡ āĻ¯āĻž āĻĢāĻžāĻ‡āĻ˛ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›āĻŋāĻ˛ index.js. āĻāĻ–āĻ¨ āĻ†āĻŽāĻ°āĻž āĻ¸ā§‡āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ•ā§āĻ°āĻŽāĻžāĻ¨ā§āĻ¸āĻžāĻ°ā§‡ āĻŦāĻŋāĻŦā§‡āĻšāĻ¨āĻž āĻ•āĻ°āĻŦāĨ¤

4. āĻ—ā§āĻ°āĻžāĻšāĻ•ā§‡āĻ° āĻ¤āĻĨā§āĻ¯ āĻŦāĻŋāĻ¨āĻŋāĻŽāĻ¯āĻŧ

āĻāĻ‡ āĻ—ā§‡āĻŽāĻŸāĻŋāĻ¤ā§‡, āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ— āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻ•āĻŸāĻŋ āĻ¸ā§āĻĒāĻ°āĻŋāĻšāĻŋāĻ¤ āĻ˛āĻžāĻ‡āĻŦā§āĻ°ā§‡āĻ°āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋ socket.io. Socket.io āĻāĻ° āĻ¨ā§‡āĻŸāĻŋāĻ­ āĻ¸āĻŽāĻ°ā§āĻĨāĻ¨ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡ āĻ“āĻ¯āĻŧā§‡āĻŦāĻ¸āĻ•ā§‡āĻŸ, āĻ¯āĻž āĻĻā§āĻŦāĻŋ-āĻŽā§āĻ–ā§€ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ—ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ‰āĻĒāĻ¯ā§āĻ•ā§āĻ¤: āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻŦāĻžāĻ°ā§āĻ¤āĻž āĻĒāĻžāĻ āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ и āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻāĻ•āĻ‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ—ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•āĻžāĻ›ā§‡ āĻŦāĻžāĻ°ā§āĻ¤āĻž āĻĒāĻžāĻ āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻĢāĻžāĻ‡āĻ˛ āĻĨāĻžāĻ•āĻŦā§‡ src/client/networking.jsāĻ•ā§‡ āĻĻā§‡āĻ–āĻ­āĻžāĻ˛ āĻ•āĻ°āĻŦā§‡ āĻ¸āĻŦāĻžāĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ—:

networking.js

import io from 'socket.io-client';
import { processGameUpdate } from './state';

const Constants = require('../shared/constants');

const socket = io(`ws://${window.location.host}`);
const connectedPromise = new Promise(resolve => {
  socket.on('connect', () => {
    console.log('Connected to server!');
    resolve();
  });
});

export const connect = onGameOver => (
  connectedPromise.then(() => {
    // Register callbacks
    socket.on(Constants.MSG_TYPES.GAME_UPDATE, processGameUpdate);
    socket.on(Constants.MSG_TYPES.GAME_OVER, onGameOver);
  })
);

export const play = username => {
  socket.emit(Constants.MSG_TYPES.JOIN_GAME, username);
};

export const updateDirection = dir => {
  socket.emit(Constants.MSG_TYPES.INPUT, dir);
};

āĻāĻ‡ āĻ•ā§‹āĻĄāĻŸāĻŋāĻ“ āĻ¸ā§āĻŦāĻšā§āĻ›āĻ¤āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸āĻžāĻŽāĻžāĻ¨ā§āĻ¯ āĻ¸āĻ‚āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤

āĻāĻ‡ āĻĢāĻžāĻ‡āĻ˛āĻŸāĻŋāĻ¤ā§‡ āĻ¤āĻŋāĻ¨āĻŸāĻŋ āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡:

  • āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ•āĻ°āĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°āĻ›āĻŋāĨ¤ connectedPromise āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ¯āĻ–āĻ¨ āĻ†āĻŽāĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻŋ āĻ¤āĻ–āĻ¨āĻ‡ āĻ…āĻ¨ā§āĻŽā§‹āĻĻāĻŋāĻ¤ā§ˇ
  • āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ¸āĻĢāĻ˛ āĻšāĻ˛ā§‡, āĻ†āĻŽāĻ°āĻž āĻ•āĻ˛āĻŦā§āĻ¯āĻžāĻ• āĻĢāĻžāĻ‚āĻļāĻ¨ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°āĻŋ (processGameUpdate() и onGameOver()) āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻŦāĻžāĻ°ā§āĻ¤āĻž āĻĒā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋāĨ¤
  • āĻ†āĻŽāĻ°āĻž āĻ°āĻĒā§āĻ¤āĻžāĻ¨āĻŋ āĻ•āĻ°āĻŋ play() и updateDirection()āĻ¯āĻžāĻ¤ā§‡ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻĢāĻžāĻ‡āĻ˛ āĻ¤āĻžāĻĻā§‡āĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤

5. āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚

āĻāĻŸāĻŋ āĻĒāĻ°ā§āĻĻāĻžāĻ¯āĻŧ āĻ›āĻŦāĻŋ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ!

â€ĻāĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻ†āĻŽāĻ°āĻž āĻ¤āĻž āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻžāĻ° āĻ†āĻ—ā§‡, āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§€āĻ¯āĻŧ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ›āĻŦāĻŋ (āĻ¸āĻŽā§āĻĒāĻĻ) āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻŦā§‡āĨ¤ āĻ†āĻ¸ā§āĻ¨ āĻāĻ•āĻŸāĻŋ āĻ°āĻŋāĻ¸ā§‹āĻ°ā§āĻ¸ āĻŽā§āĻ¯āĻžāĻ¨ā§‡āĻœāĻžāĻ° āĻ˛āĻŋāĻ–āĻŋ:

assets.js

const ASSET_NAMES = ['ship.svg', 'bullet.svg'];

const assets = {};
const downloadPromise = Promise.all(ASSET_NAMES.map(downloadAsset));

function downloadAsset(assetName) {
  return new Promise(resolve => {
    const asset = new Image();
    asset.onload = () => {
      console.log(`Downloaded ${assetName}`);
      assets[assetName] = asset;
      resolve();
    };
    asset.src = `/assets/${assetName}`;
  });
}

export const downloadAssets = () => downloadPromise;
export const getAsset = assetName => assets[assetName];

āĻ¸āĻŽā§āĻĒāĻĻ āĻŦā§āĻ¯āĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•āĻ°āĻž āĻ•āĻ āĻŋāĻ¨ āĻ¨āĻ¯āĻŧ! āĻŽā§‚āĻ˛ āĻ§āĻžāĻ°āĻŖāĻž āĻāĻ•āĻŸāĻŋ āĻŦāĻ¸ā§āĻ¤ā§ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ assets, āĻ¯āĻž āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽā§‡āĻ° āĻ•ā§€āĻŸāĻŋāĻ•ā§‡ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡āĻ° āĻŽāĻžāĻ¨ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ†āĻŦāĻĻā§āĻ§ āĻ•āĻ°āĻŦā§‡ Image. āĻ¯āĻ–āĻ¨ āĻ°āĻŋāĻ¸ā§‹āĻ°ā§āĻ¸ āĻ˛ā§‹āĻĄ āĻšāĻ¯āĻŧ, āĻ¤āĻ–āĻ¨ āĻ†āĻŽāĻ°āĻž āĻāĻŸāĻŋ āĻāĻ•āĻŸāĻŋ āĻŦāĻ¸ā§āĻ¤ā§āĻ¤ā§‡ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻŋ assets āĻ­āĻŦāĻŋāĻˇā§āĻ¯āĻ¤ā§‡ āĻĻā§āĻ°ā§āĻ¤ āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ā§‡āĻ° āĻœāĻ¨ā§āĻ¯āĨ¤ āĻ•āĻ–āĻ¨ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻĒā§ƒāĻĨāĻ• āĻ¸āĻ‚āĻ¸ā§āĻĨāĻžāĻ¨ āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ āĻĻā§‡āĻ“āĻ¯āĻŧāĻž āĻšāĻŦā§‡ (āĻ…āĻ°ā§āĻĨāĻžā§Ž, āĻ¸āĻŦ āĻ¸āĻŽā§āĻĒāĻĻ), āĻ†āĻŽāĻ°āĻž āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ āĻĻāĻŋāĻ‡ downloadPromise.

āĻ¸āĻ‚āĻ¸ā§āĻĨāĻžāĻ¨āĻ—ā§āĻ˛āĻŋ āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻĒāĻ°ā§‡, āĻ†āĻĒāĻ¨āĻŋ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻļā§āĻ°ā§ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤ āĻ†āĻ—ā§‡ āĻ¯ā§‡āĻŽāĻ¨ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡, āĻ“āĻ¯āĻŧā§‡āĻŦ āĻĒā§‡āĻœā§‡ āĻ†āĻāĻ•āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻŽāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋ HTML5 āĻ•ā§āĻ¯āĻžāĻ¨āĻ­āĻžāĻ¸ (<canvas>) āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ—ā§‡āĻŽāĻŸāĻŋ āĻŦā§‡āĻļ āĻ¸āĻšāĻœ, āĻ¤āĻžāĻ‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻ–āĻŋāĻ¤āĻ—ā§āĻ˛āĻŋ āĻ†āĻāĻ•āĻ¤ā§‡ āĻšāĻŦā§‡:

  1. āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ
  2. āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻœāĻžāĻšāĻžāĻœ
  3. āĻ–ā§‡āĻ˛āĻžāĻ° āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻ°āĻž
  4. āĻļā§‡āĻ˛

āĻāĻ–āĻžāĻ¨ā§‡ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ¸ā§āĻ¨āĻŋāĻĒā§‡āĻŸ āĻ†āĻ›ā§‡ src/client/render.js, āĻ¯āĻž āĻ‰āĻĒāĻ°ā§‡ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻžāĻ­ā§āĻ•ā§āĻ¤ āĻšāĻžāĻ°āĻŸāĻŋ āĻ†āĻ‡āĻŸā§‡āĻŽāĻ•ā§‡ āĻ āĻŋāĻ• āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°ā§‡:

render.js

import { getAsset } from './assets';
import { getCurrentState } from './state';

const Constants = require('../shared/constants');
const { PLAYER_RADIUS, PLAYER_MAX_HP, BULLET_RADIUS, MAP_SIZE } = Constants;

// Get the canvas graphics context
const canvas = document.getElementById('game-canvas');
const context = canvas.getContext('2d');

// Make the canvas fullscreen
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

function render() {
  const { me, others, bullets } = getCurrentState();
  if (!me) {
    return;
  }

  // Draw background
  renderBackground(me.x, me.y);

  // Draw all bullets
  bullets.forEach(renderBullet.bind(null, me));

  // Draw all players
  renderPlayer(me, me);
  others.forEach(renderPlayer.bind(null, me));
}

// ... Helper functions here excluded

let renderInterval = null;
export function startRendering() {
  renderInterval = setInterval(render, 1000 / 60);
}
export function stopRendering() {
  clearInterval(renderInterval);
}

āĻāĻ‡ āĻ•ā§‹āĻĄāĻŸāĻŋāĻ“ āĻ¸ā§āĻŦāĻšā§āĻ›āĻ¤āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸āĻ‚āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤

render() āĻāĻ‡ āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻ•āĻžāĻœāĨ¤ startRendering() и stopRendering() 60 FPS āĻ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ˛ā§āĻĒā§‡āĻ° āĻ¸āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻ•āĻ°āĻŖ āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ•āĻ°ā§āĻ¨āĨ¤

āĻ¸ā§āĻŦāĻ¤āĻ¨ā§āĻ¤ā§āĻ° āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻšā§‡āĻ˛ā§āĻĒāĻžāĻ° āĻĢāĻžāĻ‚āĻļāĻ¨ā§‡āĻ° āĻ•āĻ‚āĻ•ā§āĻ°āĻŋāĻŸ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ (āĻ¯ā§‡āĻŽāĻ¨ renderBullet()) āĻāĻŸāĻŋ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ¨āĻ¯āĻŧ, āĻ¤āĻŦā§‡ āĻāĻ–āĻžāĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻ¸āĻžāĻ§āĻžāĻ°āĻŖ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡:

render.js

function renderBullet(me, bullet) {
  const { x, y } = bullet;
  context.drawImage(
    getAsset('bullet.svg'),
    canvas.width / 2 + x - me.x - BULLET_RADIUS,
    canvas.height / 2 + y - me.y - BULLET_RADIUS,
    BULLET_RADIUS * 2,
    BULLET_RADIUS * 2,
  );
}

āĻŽāĻ¨ā§‡ āĻ°āĻžāĻ–āĻŦā§‡āĻ¨ āĻ¯ā§‡ āĻ†āĻŽāĻ°āĻž āĻĒāĻĻā§āĻ§āĻ¤āĻŋāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ›āĻŋ getAsset(), āĻ¯āĻž āĻ†āĻ—ā§‡ āĻĻā§‡āĻ–āĻž āĻ—āĻŋāĻ¯āĻŧā§‡āĻ›āĻŋāĻ˛ asset.js!

āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻ¸āĻšāĻžāĻ¯āĻŧāĻ• āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻœāĻžāĻ¨āĻ¤ā§‡ āĻ†āĻ—ā§āĻ°āĻšā§€ āĻšāĻ¨, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻŦāĻžāĻ•āĻŋāĻŸāĻž āĻĒāĻĄāĻŧā§āĻ¨āĨ¤ src/client/render.js.

6. āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ‡āĻ¨āĻĒā§āĻŸ

āĻāĻŸāĻŋ āĻāĻ•āĻŸāĻŋ āĻ–ā§‡āĻ˛āĻž āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻ–ā§‡āĻ˛āĻžāĻ° āĻ¯ā§‹āĻ—ā§āĻ¯! āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ¸ā§āĻ•āĻŋāĻŽāĻŸāĻŋ āĻ–ā§āĻŦ āĻ¸āĻšāĻœ āĻšāĻŦā§‡: āĻšāĻ˛āĻžāĻšāĻ˛ā§‡āĻ° āĻĻāĻŋāĻ• āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°āĻ¤ā§‡, āĻ†āĻĒāĻ¨āĻŋ āĻŽāĻžāĻ‰āĻ¸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ (āĻāĻ•āĻŸāĻŋ āĻ•āĻŽā§āĻĒāĻŋāĻ‰āĻŸāĻžāĻ°ā§‡) āĻŦāĻž āĻ¸ā§āĻ•ā§āĻ°ā§€āĻ¨āĻŸāĻŋ āĻ¸ā§āĻĒāĻ°ā§āĻļ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ (āĻāĻ•āĻŸāĻŋ āĻŽā§‹āĻŦāĻžāĻ‡āĻ˛ āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡)āĨ¤ āĻāĻŸāĻŋ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ā§‡āĻ° āĻœāĻ¨ā§āĻ¯, āĻ†āĻŽāĻ°āĻž āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°āĻŦ āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻļā§āĻ°ā§‹āĻ¤āĻž āĻŽāĻžāĻ‰āĻ¸ āĻāĻŦāĻ‚ āĻŸāĻžāĻš āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻœāĻ¨ā§āĻ¯āĨ¤
āĻāĻ¸āĻŦ āĻĻā§‡āĻ–āĻ­āĻžāĻ˛ āĻ•āĻ°āĻŦā§‡ src/client/input.js:

input.js

import { updateDirection } from './networking';

function onMouseInput(e) {
  handleInput(e.clientX, e.clientY);
}

function onTouchInput(e) {
  const touch = e.touches[0];
  handleInput(touch.clientX, touch.clientY);
}

function handleInput(x, y) {
  const dir = Math.atan2(x - window.innerWidth / 2, window.innerHeight / 2 - y);
  updateDirection(dir);
}

export function startCapturingInput() {
  window.addEventListener('mousemove', onMouseInput);
  window.addEventListener('touchmove', onTouchInput);
}

export function stopCapturingInput() {
  window.removeEventListener('mousemove', onMouseInput);
  window.removeEventListener('touchmove', onTouchInput);
}

onMouseInput() и onTouchInput() āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻļā§āĻ°ā§‹āĻ¤āĻž āĻ¯ā§‡ āĻ•āĻ˛ updateDirection() (āĻāĻ° networking.js) āĻ¯āĻ–āĻ¨ āĻāĻ•āĻŸāĻŋ āĻ‡āĻ¨āĻĒā§āĻŸ āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻ˜āĻŸā§‡ (āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻ¸ā§āĻŦāĻ°ā§‚āĻĒ, āĻ¯āĻ–āĻ¨ āĻŽāĻžāĻ‰āĻ¸ āĻ¸āĻ°āĻžāĻ¨ā§‹ āĻšāĻ¯āĻŧ)āĨ¤ updateDirection() āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻŽā§‡āĻ¸ā§‡āĻœāĻŋāĻ‚ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§‡, āĻ¯āĻž āĻ‡āĻ¨āĻĒā§āĻŸ āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻ¸ā§‡āĻ‡ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¯āĻŧā§€ āĻ—ā§‡āĻŽā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻž āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°ā§‡āĨ¤

7. āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ¸ā§āĻŸā§āĻ¯āĻžāĻŸāĻžāĻ¸

āĻāĻ‡ āĻŦāĻŋāĻ­āĻžāĻ—āĻŸāĻŋ āĻĒā§‹āĻ¸ā§āĻŸā§‡āĻ° āĻĒā§āĻ°āĻĨāĻŽ āĻ…āĻ‚āĻļā§‡ āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ•āĻ āĻŋāĻ¨āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻĒā§āĻ°āĻĨāĻŽāĻŦāĻžāĻ° āĻāĻŸāĻŋ āĻĒāĻĄāĻŧāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻāĻŸāĻŋ āĻŦā§āĻāĻ¤ā§‡ āĻ¨āĻž āĻĒāĻžāĻ°ā§‡āĻ¨ āĻ¤āĻŦā§‡ āĻšāĻ¤āĻžāĻļ āĻšāĻŦā§‡āĻ¨ āĻ¨āĻž! āĻāĻŽāĻ¨āĻ•āĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻāĻŸāĻŋ āĻāĻĄāĻŧāĻŋāĻ¯āĻŧā§‡ āĻ¯ā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ āĻāĻŦāĻ‚ āĻĒāĻ°ā§‡ āĻāĻŸāĻŋāĻ¤ā§‡ āĻĢāĻŋāĻ°ā§‡ āĻ†āĻ¸āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤

āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ/āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ•ā§‹āĻĄāĻŸāĻŋ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§€āĻ¯āĻŧ āĻ§āĻžāĻāĻ§āĻžāĻ° āĻļā§‡āĻˇ āĻ…āĻ‚āĻļāĻŸāĻŋ āĻ…āĻŦāĻ¸ā§āĻĨāĻž. āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻŦāĻŋāĻ­āĻžāĻ— āĻĨā§‡āĻ•ā§‡ āĻ•ā§‹āĻĄ āĻ¸ā§āĻ¨āĻŋāĻĒā§‡āĻŸ āĻŽāĻ¨ā§‡ āĻ†āĻ›ā§‡?

render.js

import { getCurrentState } from './state';

function render() {
  const { me, others, bullets } = getCurrentState();

  // Do the rendering
  // ...
}

getCurrentState() āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ—ā§‡āĻŽā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ…āĻŦāĻ¸ā§āĻĨāĻž āĻĻāĻŋāĻ¤ā§‡ āĻ¸āĻ•ā§āĻˇāĻŽ āĻšāĻ“āĻ¯āĻŧāĻž āĻ‰āĻšāĻŋāĻ¤ āĻ¯ā§‡ āĻ•ā§‹āĻ¨ āĻ¸āĻŽāĻ¯āĻŧā§‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻ†āĻĒāĻĄā§‡āĻŸā§‡āĻ° āĻ‰āĻĒāĻ° āĻ­āĻŋāĻ¤ā§āĻ¤āĻŋ āĻ•āĻ°ā§‡āĨ¤ āĻāĻ–āĻžāĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ āĻ¯āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĒāĻžāĻ āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡:

{
  "t": 1555960373725,
  "me": {
    "x": 2213.8050880413657,
    "y": 1469.370893425012,
    "direction": 1.3082443894581433,
    "id": "AhzgAtklgo2FJvwWAADO",
    "hp": 100
  },
  "others": [],
  "bullets": [
    {
      "id": "RUJfJ8Y18n",
      "x": 2354.029197099604,
      "y": 1431.6848318262666
    },
    {
      "id": "ctg5rht5s",
      "x": 2260.546457727445,
      "y": 1456.8088728920968
    }
  ],
  "leaderboard": [
    {
      "username": "Player",
      "score": 3
    }
  ]
}

āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡ āĻĒāĻžāĻāĻšāĻŸāĻŋ āĻ…āĻ­āĻŋāĻ¨ā§āĻ¨ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ° āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡:

  • t: āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ āĻ¨āĻŋāĻ°ā§āĻĻā§‡āĻļ āĻ•āĻ°ā§‡ āĻ¯ā§‡ āĻāĻ‡ āĻ†āĻĒāĻĄā§‡āĻŸāĻŸāĻŋ āĻ•āĻ–āĻ¨ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›āĻŋāĻ˛ā§ˇ
  • me: āĻāĻ‡ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ¤āĻĨā§āĻ¯.
  • āĻ…āĻ¨ā§āĻ¯āĻĻā§‡āĻ°: āĻāĻ•āĻ‡ āĻ–ā§‡āĻ˛āĻžāĻ¯āĻŧ āĻ…āĻ‚āĻļāĻ—ā§āĻ°āĻšāĻŖāĻ•āĻžāĻ°ā§€ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ¤āĻĨā§āĻ¯ā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻŦāĻŋāĻ¨ā§āĻ¯āĻžāĻ¸āĨ¤
  • āĻŦā§āĻ˛ā§‡āĻŸ: āĻ—ā§‡āĻŽā§‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ¤āĻĨā§āĻ¯ā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻŦāĻŋāĻ¨ā§āĻ¯āĻžāĻ¸āĨ¤
  • āĻ˛āĻŋāĻĄāĻžāĻ°āĻŦā§‹āĻ°ā§āĻĄā§‡: āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ˛āĻŋāĻĄāĻžāĻ°āĻŦā§‹āĻ°ā§āĻĄ āĻĄā§‡āĻŸāĻžāĨ¤ āĻāĻ‡ āĻĒā§‹āĻ¸ā§āĻŸā§‡, āĻ†āĻŽāĻ°āĻž āĻ¤āĻžāĻĻā§‡āĻ° āĻŦāĻŋāĻŦā§‡āĻšāĻ¨āĻž āĻ•āĻ°āĻŦ āĻ¨āĻžāĨ¤

7.1 āĻ¨āĻŋāĻˇā§āĻĒāĻžāĻĒ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻž

āĻ¨āĻŋāĻˇā§āĻĒāĻžāĻĒ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ getCurrentState() āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ¸āĻžāĻŽā§āĻĒā§āĻ°āĻ¤āĻŋāĻ• āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡āĻ° āĻĄā§‡āĻŸāĻž āĻ¸āĻ°āĻžāĻ¸āĻ°āĻŋ āĻĢā§‡āĻ°āĻ¤ āĻĻāĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤

naive-state.js

let lastGameUpdate = null;

// Handle a newly received game update.
export function processGameUpdate(update) {
  lastGameUpdate = update;
}

export function getCurrentState() {
  return lastGameUpdate;
}

āĻšāĻŽā§ŽāĻ•āĻžāĻ° āĻāĻŦāĻ‚ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ°! āĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻ¯āĻĻāĻŋ āĻāĻŸāĻž āĻ¸āĻšāĻœ āĻ›āĻŋāĻ˛. āĻāĻ‡ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻžāĻ°āĻŖāĻ—ā§āĻ˛āĻŋāĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻāĻ•āĻŸāĻŋ: āĻāĻŸāĻŋ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻĢā§āĻ°ā§‡āĻŽ āĻ°ā§‡āĻŸāĻ•ā§‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ˜āĻĄāĻŧāĻŋāĻ° āĻšāĻžāĻ°ā§‡ āĻ¸ā§€āĻŽāĻžāĻŦāĻĻā§āĻ§ āĻ•āĻ°ā§‡.

āĻšāĻ•ā§āĻ°ā§‡āĻ° āĻšāĻžāĻ°: āĻĢā§āĻ°ā§‡āĻŽā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž (āĻ¯ā§‡āĻŽāĻ¨ āĻ•āĻ˛ render()) āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ, āĻŦāĻž FPSāĨ¤ āĻ—ā§‡āĻŽāĻ—ā§āĻ˛āĻŋ āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻ•āĻŽāĻĒāĻ•ā§āĻˇā§‡ 60 FPS āĻ…āĻ°ā§āĻœāĻ¨ āĻ•āĻ°āĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§‡āĨ¤

āĻŸāĻŋāĻ• āĻ°ā§‡āĻŸ: āĻ¯ā§‡ āĻĢā§āĻ°āĻŋāĻ•ā§‹āĻ¯āĻŧā§‡āĻ¨ā§āĻ¸āĻŋāĻ¤ā§‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸāĻĻā§‡āĻ° āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ āĻžāĻ¯āĻŧāĨ¤ āĻāĻŸāĻŋ āĻĒā§āĻ°āĻžāĻ¯āĻŧāĻļāĻ‡ āĻĢā§āĻ°ā§‡āĻŽā§‡āĻ° āĻšāĻžāĻ°ā§‡āĻ° āĻšā§‡āĻ¯āĻŧā§‡ āĻ•āĻŽ āĻšāĻ¯āĻŧ. āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ—ā§‡āĻŽā§‡, āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°āĻŸāĻŋ āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ 30 āĻšāĻ•ā§āĻ°ā§‡āĻ° āĻĢā§āĻ°āĻŋāĻ•ā§‹āĻ¯āĻŧā§‡āĻ¨ā§āĻ¸āĻŋāĻ¤ā§‡ āĻšāĻ˛ā§‡āĨ¤

āĻ†āĻŽāĻ°āĻž āĻ¯āĻĻāĻŋ āĻ—ā§‡āĻŽā§‡āĻ° āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻŋ, āĻ¤āĻžāĻšāĻ˛ā§‡ FPS āĻ•āĻ–āĻ¨āĻ‡ 30-āĻāĻ° āĻŦā§‡āĻļāĻŋ āĻšāĻŦā§‡ āĻ¨āĻž, āĻ•āĻžāĻ°āĻŖ āĻ†āĻŽāĻ°āĻž āĻ•āĻ–āĻ¨āĻ‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ ā§Šā§ĻāĻŸāĻŋāĻ° āĻŦā§‡āĻļāĻŋ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ‡ āĻ¨āĻž. āĻ†āĻŽāĻ°āĻž āĻĄāĻžāĻ•āĻ˛ā§‡āĻ“ render() āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ 60 āĻŦāĻžāĻ°, āĻ¤āĻžāĻ°āĻĒāĻ° āĻāĻ‡ āĻ•āĻ˛āĻ—ā§āĻ˛āĻŋāĻ° āĻ…āĻ°ā§āĻ§ā§‡āĻ• āĻāĻ•āĻ‡ āĻœāĻŋāĻ¨āĻŋāĻ¸ āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ†āĻāĻ•āĻŦā§‡, āĻŽā§‚āĻ˛āĻ¤ āĻ•āĻŋāĻ›ā§āĻ‡ āĻ•āĻ°āĻŦā§‡ āĻ¨āĻžāĨ¤ āĻ¨āĻŋāĻˇā§āĻĒāĻžāĻĒ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ†āĻ°ā§‡āĻ•āĻŸāĻŋ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻž āĻšāĻ˛ āĻ¯ā§‡ āĻāĻŸāĻŋ āĻŦāĻŋāĻ˛āĻŽā§āĻŦ āĻĒā§āĻ°āĻŦāĻŖ. āĻāĻ•āĻŸāĻŋ āĻ†āĻĻāĻ°ā§āĻļ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻ¨ā§‡āĻŸ āĻ—āĻ¤āĻŋāĻ° āĻ¸āĻžāĻĨā§‡, āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻĒā§āĻ°āĻ¤āĻŋ 33ms (30 āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡) āĻāĻ•āĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻŦā§‡āĻ¨:

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻĻā§āĻ°ā§āĻ­āĻžāĻ—ā§āĻ¯āĻ•ā§āĻ°āĻŽā§‡, āĻ•āĻŋāĻ›ā§āĻ‡ āĻ¨āĻŋāĻ–ā§āĻāĻ¤ āĻ¨āĻ¯āĻŧāĨ¤ āĻāĻ•āĻŸāĻŋ āĻ†āĻ°ā§‹ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻ¸āĻŽā§āĻŽāĻ¤ āĻ›āĻŦāĻŋ āĻšāĻŦā§‡:
āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻ¨āĻŋāĻˇā§āĻĒā§āĻ°āĻ­ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•āĻžāĻ°ā§āĻ¯āĻ¤ āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ–āĻžāĻ°āĻžāĻĒ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°ā§‡ āĻ¯āĻ–āĻ¨ āĻāĻŸāĻŋ āĻŦāĻŋāĻ˛āĻŽā§āĻŦāĻŋāĻ¤ āĻšāĻ¯āĻŧ. āĻ¯āĻĻāĻŋ 50ms āĻŦāĻŋāĻ˛āĻŽā§āĻŦā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻāĻ•āĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ“āĻ¯āĻŧāĻž āĻ¯āĻžāĻ¯āĻŧ, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ¸ā§āĻŸāĻ˛ āĻāĻ•āĻŸāĻŋ āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ 50ms āĻ•āĻžāĻ°āĻŖ āĻāĻŸāĻŋ āĻāĻ–āĻ¨āĻ“ āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āĻ¤ā§€ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĨā§‡āĻ•ā§‡ āĻ—ā§‡āĻŽā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻž āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻ›ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻ˛ā§āĻĒāĻ¨āĻž āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ āĻ¯ā§‡ āĻāĻŸāĻŋ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ•āĻ¤āĻŸāĻž āĻ…āĻ¸ā§āĻŦāĻ¸ā§āĻ¤āĻŋāĻ•āĻ°: āĻ¨āĻŋāĻ°ā§āĻŦāĻŋāĻšāĻžāĻ°ā§‡ āĻŦā§āĻ°ā§‡āĻ•āĻŋāĻ‚ āĻ–ā§‡āĻ˛āĻžāĻŸāĻŋāĻ•ā§‡ āĻāĻžāĻāĻ•ā§āĻ¨āĻŋ āĻāĻŦāĻ‚ āĻ…āĻ¸ā§āĻĨāĻŋāĻ° āĻ•āĻ°ā§‡ āĻ¤ā§āĻ˛āĻŦā§‡āĨ¤

7.2 āĻ‰āĻ¨ā§āĻ¨āĻ¤ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ…āĻŦāĻ¸ā§āĻĨāĻž

āĻ†āĻŽāĻ°āĻž āĻ¨āĻŋāĻˇā§āĻĒāĻžāĻĒ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•āĻŋāĻ›ā§ āĻ‰āĻ¨ā§āĻ¨āĻ¤āĻŋ āĻ•āĻ°āĻž āĻšāĻŦā§‡. āĻĒā§āĻ°āĻĨāĻŽāĻ¤, āĻ†āĻŽāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻŦāĻŋāĻ˛āĻŽā§āĻŦ 100 ms āĻœāĻ¨ā§āĻ¯ āĻāĻ° āĻŽāĻžāĻ¨ā§‡ āĻšāĻ˛ āĻ¯ā§‡ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° "āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨" āĻ…āĻŦāĻ¸ā§āĻĨāĻž āĻ¸āĻ°ā§āĻŦāĻĻāĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻ—ā§‡āĻŽā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻž āĻĨā§‡āĻ•ā§‡ 100ms āĻĒāĻŋāĻ›āĻŋāĻ¯āĻŧā§‡ āĻĨāĻžāĻ•āĻŦā§‡āĨ¤ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻ¸ā§āĻŦāĻ°ā§‚āĻĒ, āĻ¯āĻĻāĻŋ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻ¸āĻŽāĻ¯āĻŧ āĻĨāĻžāĻ•ā§‡ 150, āĻ¤āĻžāĻ°āĻĒāĻ° āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ¸ā§‡āĻ‡ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¯āĻŧ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻŦā§‡ āĻ¯ā§‡ āĻ¸āĻŽāĻ¯āĻŧā§‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°āĻŸāĻŋ āĻ›āĻŋāĻ˛ 50:

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻāĻŸāĻŋ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻāĻ•āĻŸāĻŋ 100ms āĻŦāĻžāĻĢāĻžāĻ° āĻĻā§‡āĻ¯āĻŧ āĻ…āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻļāĻŋāĻ¤ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ¸āĻŽāĻ¯āĻŧ āĻŦā§‡āĻāĻšā§‡ āĻĨāĻžāĻ•āĻžāĻ° āĻœāĻ¨ā§āĻ¯:

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻĒāĻžāĻ“āĻ¨āĻž āĻ¸ā§āĻĨāĻžāĻ¯āĻŧā§€ āĻšāĻŦā§‡ āĻ‡āĻ¨āĻĒā§āĻŸ āĻ˛ā§āĻ¯āĻžāĻ— 100 ms āĻœāĻ¨ā§āĻ¯ āĻŽāĻ¸ā§ƒāĻŖ āĻ—ā§‡āĻŽāĻĒā§āĻ˛ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻŸāĻŋ āĻāĻ•āĻŸāĻŋ āĻ›ā§‹āĻŸāĻ–āĻžāĻŸā§‹ āĻ¤ā§āĻ¯āĻžāĻ— - āĻŦā§‡āĻļāĻŋāĻ°āĻ­āĻžāĻ— āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧ (āĻŦāĻŋāĻļā§‡āĻˇāĻ¤ āĻ¨ā§ˆāĻŽāĻŋāĻ¤ā§āĻ¤āĻŋāĻ• āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧ) āĻāĻŽāĻ¨āĻ•āĻŋ āĻāĻ‡ āĻŦāĻŋāĻ˛āĻŽā§āĻŦāĻŸāĻŋ āĻ˛āĻ•ā§āĻˇā§āĻ¯ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻžāĨ¤ āĻāĻŸāĻŋ āĻāĻ•āĻŸāĻŋ āĻ…āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻļāĻŋāĻ¤ āĻŦāĻŋāĻ˛āĻŽā§āĻŦā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ–ā§‡āĻ˛āĻžāĻ° āĻšā§‡āĻ¯āĻŧā§‡ āĻāĻ•āĻŸāĻŋ āĻ¸ā§āĻĨāĻŋāĻ° 100ms āĻ˛ā§‡āĻŸā§‡āĻ¨ā§āĻ¸āĻŋāĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻžāĻŽāĻžā§āĻœāĻ¸ā§āĻ¯ āĻ•āĻ°āĻž āĻ…āĻ¨ā§‡āĻ• āĻ¸āĻšāĻœāĨ¤

āĻ†āĻŽāĻ°āĻž āĻ¨āĻžāĻŽāĻ• āĻ†āĻ°ā§‡āĻ•āĻŸāĻŋ āĻ•ā§ŒāĻļāĻ˛āĻ“ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ-āĻ¸āĻžāĻ‡āĻĄ āĻĒā§‚āĻ°ā§āĻŦāĻžāĻ­āĻžāĻ¸, āĻ¯āĻž āĻ…āĻ¨ā§āĻ­ā§‚āĻ¤ āĻ˛ā§‡āĻŸā§‡āĻ¨ā§āĻ¸āĻŋ āĻ•āĻŽāĻžāĻ¤ā§‡ āĻāĻ•āĻŸāĻŋ āĻ­āĻžāĻ˛ āĻ•āĻžāĻœ āĻ•āĻ°ā§‡, āĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻāĻ‡ āĻĒā§‹āĻ¸ā§āĻŸā§‡ āĻ•āĻ­āĻžāĻ° āĻ•āĻ°āĻž āĻšāĻŦā§‡ āĻ¨āĻžāĨ¤

āĻ†āĻ°ā§‡āĻ•āĻŸāĻŋ āĻ‰āĻ¨ā§āĻ¨āĻ¤āĻŋ āĻ†āĻŽāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ›āĻŋ āĻ°ā§ˆāĻ–āĻŋāĻ• āĻ•ā§āĻˇā§‡āĻĒāĻ•. āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻ˛ā§āĻ¯āĻžāĻ—ā§‡āĻ° āĻ•āĻžāĻ°āĻŖā§‡, āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻĨā§‡āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¤āĻ¤ āĻāĻ•āĻŸāĻŋ āĻ†āĻĒāĻĄā§‡āĻŸ āĻāĻ—āĻŋāĻ¯āĻŧā§‡ āĻĨāĻžāĻ•āĻŋāĨ¤ āĻĄāĻžāĻ•āĻ˛ā§‡ getCurrentState(), āĻ†āĻŽāĻ°āĻž āĻšāĻžāĻ˛āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ āĻ°ā§ˆāĻ–āĻŋāĻ• āĻ•ā§āĻˇā§‡āĻĒāĻ• āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻ āĻŋāĻ• āĻ†āĻ—ā§‡ āĻāĻŦāĻ‚ āĻĒāĻ°ā§‡ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡:

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻāĻŸāĻŋ āĻĢā§āĻ°ā§‡āĻŽ āĻ°ā§‡āĻŸ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻ° āĻ¸āĻŽāĻžāĻ§āĻžāĻ¨ āĻ•āĻ°ā§‡: āĻ†āĻŽāĻ°āĻž āĻāĻ–āĻ¨ āĻ¯ā§‡ āĻ•ā§‹āĻ¨ā§‹ āĻĢā§āĻ°ā§‡āĻŽ āĻšāĻžāĻ°ā§‡ āĻ…āĻ¨āĻ¨ā§āĻ¯ āĻĢā§āĻ°ā§‡āĻŽ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ!

7.3 āĻ‰āĻ¨ā§āĻ¨āĻ¤ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ°āĻžāĻˇā§āĻŸā§āĻ° āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨

āĻŽāĻ§ā§āĻ¯ā§‡ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ src/client/state.js āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ˛ā§āĻ¯āĻžāĻ— āĻāĻŦāĻ‚ āĻ˛āĻŋāĻ¨āĻŋāĻ¯āĻŧāĻžāĻ° āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻĒā§‹āĻ˛ā§‡āĻļāĻ¨ āĻ‰āĻ­āĻ¯āĻŧāĻ‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡, āĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻĻā§€āĻ°ā§āĻ˜ āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ¨āĻ¯āĻŧāĨ¤ āĻ•ā§‹āĻĄāĻŸāĻŋāĻ•ā§‡ āĻĻā§āĻŸāĻŋ āĻ­āĻžāĻ—ā§‡ āĻ­āĻžāĻ— āĻ•āĻ°āĻž āĻ¯āĻžāĻ•āĨ¤ āĻāĻ–āĻžāĻ¨ā§‡ āĻĒā§āĻ°āĻĨāĻŽ āĻāĻ•āĻŸāĻŋ:

state.js āĻ…āĻ‚āĻļ 1

const RENDER_DELAY = 100;

const gameUpdates = [];
let gameStart = 0;
let firstServerTimestamp = 0;

export function initState() {
  gameStart = 0;
  firstServerTimestamp = 0;
}

export function processGameUpdate(update) {
  if (!firstServerTimestamp) {
    firstServerTimestamp = update.t;
    gameStart = Date.now();
  }
  gameUpdates.push(update);

  // Keep only one game update before the current server time
  const base = getBaseUpdate();
  if (base > 0) {
    gameUpdates.splice(0, base);
  }
}

function currentServerTime() {
  return firstServerTimestamp + (Date.now() - gameStart) - RENDER_DELAY;
}

// Returns the index of the base update, the first game update before
// current server time, or -1 if N/A.
function getBaseUpdate() {
  const serverTime = currentServerTime();
  for (let i = gameUpdates.length - 1; i >= 0; i--) {
    if (gameUpdates[i].t <= serverTime) {
      return i;
    }
  }
  return -1;
}

āĻĒā§āĻ°āĻĨāĻŽ āĻ§āĻžāĻĒ āĻšāĻ˛ āĻ•āĻŋ āĻŦā§‡āĻ° āĻ•āĻ°āĻž currentServerTime(). āĻ†āĻŽāĻ°āĻž āĻ†āĻ—ā§‡ āĻĻā§‡āĻ–ā§‡āĻ›āĻŋ, āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡ āĻāĻ•āĻŸāĻŋ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ āĻ…āĻ¨ā§āĻ¤āĻ°ā§āĻ­ā§āĻ•ā§āĻ¤ āĻĨāĻžāĻ•ā§‡āĨ¤ āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻĒāĻŋāĻ›āĻ¨ā§‡ 100ms āĻ‡āĻŽā§‡āĻœ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ˛ā§‡āĻŸā§‡āĻ¨ā§āĻ¸āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ‡, āĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻ†āĻŽāĻ°āĻž āĻ•āĻ–āĻ¨āĻ‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻœāĻžāĻ¨āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦ āĻ¨āĻž, āĻ•āĻžāĻ°āĻŖ āĻ†āĻŽāĻ°āĻž āĻœāĻžāĻ¨āĻŋ āĻ¨āĻž āĻ¯ā§‡ āĻ•ā§‹āĻ¨ā§‹ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•āĻžāĻ›ā§‡ āĻĒā§‡āĻ¤ā§‡ āĻ•āĻ¤āĻ•ā§āĻˇāĻŖ āĻ˛ā§‡āĻ—ā§‡āĻ›ā§‡āĨ¤ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻ¨ā§‡āĻŸ āĻ…āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻļāĻŋāĻ¤ āĻāĻŦāĻ‚ āĻāĻ° āĻ—āĻ¤āĻŋ āĻŦā§āĻ¯āĻžāĻĒāĻ•āĻ­āĻžāĻŦā§‡ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻšāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡!

āĻāĻ‡ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻŸāĻŋ āĻĒā§‡āĻ¤ā§‡, āĻ†āĻŽāĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻ¯ā§āĻ•ā§āĻ¤āĻŋāĻ¸āĻ™ā§āĻ—āĻ¤ āĻ…āĻ¨ā§āĻŽāĻžāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ: āĻ†āĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ…āĻŦāĻŋāĻ˛āĻŽā§āĻŦā§‡ āĻ†āĻ—āĻ¤ āĻ­āĻžāĻ¨. āĻ¯āĻĻāĻŋ āĻāĻŸāĻŋ āĻ¸āĻ¤ā§āĻ¯ āĻšāĻ¯āĻŧ, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻ†āĻŽāĻ°āĻž āĻāĻ‡ āĻŦāĻŋāĻļā§‡āĻˇ āĻŽā§āĻšā§‚āĻ°ā§āĻ¤ā§‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻœāĻžāĻ¨āĻ¤ā§‡ āĻĒāĻžāĻ°āĻ¤āĻžāĻŽ! āĻ†āĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻŋ firstServerTimestamp āĻāĻŦāĻ‚ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ°āĻžāĻ–āĻž āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ (āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ) āĻāĻ•āĻ‡ āĻŽā§āĻšā§‚āĻ°ā§āĻ¤ā§‡ āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ gameStart.

āĻ…āĻĒā§‡āĻ•ā§āĻˇāĻž āĻ•āĻ°. āĻāĻŸāĻŋ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ = āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ¸āĻŽāĻ¯āĻŧ āĻšāĻ“āĻ¯āĻŧāĻž āĻ‰āĻšāĻŋāĻ¤ āĻ¨āĻ¯āĻŧ? āĻ•ā§‡āĻ¨ āĻ†āĻŽāĻ°āĻž "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ" āĻāĻŦāĻ‚ "āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ" āĻāĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻĒāĻžāĻ°ā§āĻĨāĻ•ā§āĻ¯ āĻ•āĻ°āĻŋ? āĻāĻ‡āĻŸāĻž āĻāĻ•āĻŸāĻž āĻ­āĻžāĻ˛ā§‹ āĻĒā§āĻ°āĻļā§āĻ¨! āĻĻā§‡āĻ–āĻž āĻ¯āĻžāĻšā§āĻ›ā§‡ āĻ¯ā§‡ āĻ¤āĻžāĻ°āĻž āĻāĻ•āĻ‡ āĻœāĻŋāĻ¨āĻŋāĻ¸ āĻ¨āĻ¯āĻŧāĨ¤ Date.now() āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻāĻŦāĻ‚ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āĻ¨ āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ āĻĢāĻŋāĻ°āĻŋāĻ¯āĻŧā§‡ āĻĻā§‡āĻŦā§‡ āĻāĻŦāĻ‚ āĻāĻŸāĻŋ āĻāĻ‡ āĻŽā§‡āĻļāĻŋāĻ¨āĻ—ā§āĻ˛āĻŋāĻ° āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ āĻ•āĻžāĻ°āĻŖāĻ—ā§āĻ˛āĻŋāĻ° āĻ‰āĻĒāĻ° āĻ¨āĻŋāĻ°ā§āĻ­āĻ° āĻ•āĻ°ā§‡āĨ¤ āĻ•āĻ–āĻ¨āĻ‡ āĻ…āĻ¨ā§āĻŽāĻžāĻ¨ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž āĻ¯ā§‡ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻŽā§‡āĻļāĻŋāĻ¨ā§‡ āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ āĻāĻ•āĻ‡ āĻšāĻŦā§‡āĨ¤

āĻāĻ–āĻ¨ āĻ†āĻŽāĻ°āĻž āĻŦā§āĻāĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ āĻ•āĻŋ āĻ•āĻ°ā§‡ currentServerTime(): āĻāĻŸāĻž āĻĢāĻŋāĻ°ā§‡ āĻ†āĻ¸ā§‡ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒ. āĻ…āĻ¨ā§āĻ¯ āĻ•āĻĨāĻžāĻ¯āĻŧ, āĻāĻŸāĻŋ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ (firstServerTimestamp <+ (Date.now() - gameStart)) āĻŦāĻŋāĻ¯āĻŧā§‹āĻ— āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻŦāĻŋāĻ˛āĻŽā§āĻŦ (RENDER_DELAY).

āĻāĻ–āĻ¨ āĻ†āĻ¸ā§āĻ¨ āĻ†āĻŽāĻ°āĻž āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸāĻ—ā§āĻ˛āĻŋ āĻ•ā§€āĻ­āĻžāĻŦā§‡ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°āĻŋ āĻ¤āĻž āĻĻā§‡āĻ–ā§‡ āĻ¨ā§‡āĻ“āĻ¯āĻŧāĻž āĻ¯āĻžāĻ•āĨ¤ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻšāĻ˛ā§‡, āĻāĻŸāĻŋ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ processGameUpdate()āĻāĻŦāĻ‚ āĻ†āĻŽāĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ°ā§‡āĻ¤ā§‡ āĻ¨āĻ¤ā§āĻ¨ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻŋ gameUpdates. āĻ¤āĻžāĻ°āĻĒāĻ°, āĻŽā§‡āĻŽāĻ°āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻž āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯, āĻ†āĻŽāĻ°āĻž āĻ†āĻ—ā§‡ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻĒā§āĻ°āĻžāĻ¨ā§‹ āĻ†āĻĒāĻĄā§‡āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻŋ āĻŦā§‡āĻ¸ āĻ†āĻĒāĻĄā§‡āĻŸāĻ•āĻžāĻ°āĻŖ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ†āĻ° āĻ¤āĻžāĻĻā§‡āĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻ¨ā§‡āĻ‡āĨ¤

āĻāĻ•āĻŸāĻŋ "āĻŽā§ŒāĻ˛āĻŋāĻ• āĻ†āĻĒāĻĄā§‡āĻŸ" āĻ•āĻŋ? āĻāĻ‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻĨā§‡āĻ•ā§‡ āĻĒāĻŋāĻ›āĻ¨ā§‡ āĻ¸āĻ°ā§‡ āĻ—āĻŋāĻ¯āĻŧā§‡ āĻ†āĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻ†āĻĒāĻĄā§‡āĻŸāĻŸāĻŋ āĻĒāĻžāĻ‡. āĻāĻ‡ āĻšāĻŋāĻ¤ā§āĻ°āĻŸāĻŋ āĻŽāĻ¨ā§‡ āĻ†āĻ›ā§‡?

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
"āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻŸāĻžāĻ‡āĻŽ" āĻāĻ° āĻŦāĻžāĻŽā§‡ āĻ¸āĻ°āĻžāĻ¸āĻ°āĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸāĻŸāĻŋ āĻŦā§‡āĻ¸ āĻ†āĻĒāĻĄā§‡āĻŸāĨ¤

āĻŦā§‡āĻ¸ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻŋ āĻœāĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ āĻšāĻ¯āĻŧ? āĻ•ā§‡āĻ¨ āĻ†āĻŽāĻ°āĻž āĻŦā§‡āĻ¸āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĄā§āĻ°āĻĒ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ? āĻāĻ‡ āĻŦā§‡āĻ° āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯, āĻ†āĻ¸ā§āĻ¨ āĻĒāĻ°āĻŋāĻļā§‡āĻˇā§‡ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻŦāĻŋāĻŦā§‡āĻšāĻ¨āĻž āĻ•āĻ°ā§āĻ¨ getCurrentState():

state.js āĻ…āĻ‚āĻļ 2

export function getCurrentState() {
  if (!firstServerTimestamp) {
    return {};
  }

  const base = getBaseUpdate();
  const serverTime = currentServerTime();

  // If base is the most recent update we have, use its state.
  // Else, interpolate between its state and the state of (base + 1).
  if (base < 0) {
    return gameUpdates[gameUpdates.length - 1];
  } else if (base === gameUpdates.length - 1) {
    return gameUpdates[base];
  } else {
    const baseUpdate = gameUpdates[base];
    const next = gameUpdates[base + 1];
    const r = (serverTime - baseUpdate.t) / (next.t - baseUpdate.t);
    return {
      me: interpolateObject(baseUpdate.me, next.me, r),
      others: interpolateObjectArray(baseUpdate.others, next.others, r),
      bullets: interpolateObjectArray(baseUpdate.bullets, next.bullets, r),
    };
  }
}

āĻ†āĻŽāĻ°āĻž āĻ¤āĻŋāĻ¨āĻŸāĻŋ āĻŽāĻžāĻŽāĻ˛āĻž āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°āĻŋ:

  1. base < 0 āĻŽāĻžāĻ¨ā§‡ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻĒāĻ°ā§āĻ¯āĻ¨ā§āĻ¤ āĻ•ā§‹āĻ¨ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ¨ā§‡āĻ‡ (āĻ‰āĻĒāĻ°ā§‡āĻ° āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§‡āĻ–ā§āĻ¨ getBaseUpdate()) āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ‚ āĻ˛ā§āĻ¯āĻžāĻ—ā§‡āĻ° āĻ•āĻžāĻ°āĻŖā§‡ āĻ—ā§‡āĻŽā§‡āĻ° āĻļā§āĻ°ā§āĻ¤ā§‡ āĻāĻŸāĻŋ āĻ˜āĻŸāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ āĻāĻ‡ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°ā§‡, āĻ†āĻŽāĻ°āĻž āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ†āĻĒāĻĄā§‡āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŋāĨ¤
  2. base āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ†āĻ›ā§‡ āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ†āĻĒāĻĄā§‡āĻŸ. āĻāĻŸāĻŋ āĻ¨ā§‡āĻŸāĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻ• āĻŦāĻŋāĻ˛āĻŽā§āĻŦ āĻŦāĻž āĻĻā§āĻ°ā§āĻŦāĻ˛ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻ¨ā§‡āĻŸ āĻ¸āĻ‚āĻ¯ā§‹āĻ—ā§‡āĻ° āĻ•āĻžāĻ°āĻŖā§‡ āĻšāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ āĻāĻ‡ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°ā§‡, āĻ†āĻŽāĻ°āĻž āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ•āĻžāĻ›ā§‡ āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ†āĻĒāĻĄā§‡āĻŸāĻŸāĻŋāĻ“ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ›āĻŋāĨ¤
  3. āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§‡āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻ†āĻ—ā§‡ āĻāĻŦāĻ‚ āĻĒāĻ°ā§‡ āĻ‰āĻ­āĻ¯āĻŧāĻ‡ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ†āĻ›ā§‡, āĻ¤āĻžāĻ‡ āĻ†āĻŽāĻ°āĻž āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŋ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻĒā§‹āĻ˛ā§‡āĻŸ!

āĻ¯ā§‡ āĻ¸āĻŦ āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡ state.js āĻ˛āĻŋāĻ¨āĻŋāĻ¯āĻŧāĻžāĻ° āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻĒā§‹āĻ˛ā§‡āĻļāĻ¨ā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ¯āĻž āĻ¸āĻšāĻœ (āĻ•āĻŋāĻ¨ā§āĻ¤ā§ āĻŦāĻŋāĻ°āĻ•ā§āĻ¤āĻŋāĻ•āĻ°) āĻ—āĻŖāĻŋāĻ¤āĨ¤ āĻ¯āĻĻāĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻ¨āĻŋāĻœā§‡āĻ‡ āĻāĻŸāĻŋ āĻ…āĻ¨ā§āĻŦā§‡āĻˇāĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨, āĻ¤āĻžāĻ°āĻĒāĻ° āĻ–ā§āĻ˛ā§āĻ¨ state.js āĻ‰āĻĒāĻ° āĻ—āĻŋāĻŸāĻšāĻžāĻŦ.

āĻĒāĻžāĻ°ā§āĻŸ 2. āĻŦā§āĻ¯āĻžāĻ•āĻāĻ¨ā§āĻĄ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°

āĻāĻ‡ āĻ…āĻ‚āĻļā§‡, āĻ†āĻŽāĻ°āĻž Node.js āĻŦā§āĻ¯āĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āĻ° āĻĻāĻŋāĻ•ā§‡ āĻ¨āĻœāĻ° āĻĻā§‡āĻŦ āĻ¯āĻž āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ•āĻ°ā§‡ io āĻ—ā§‡āĻŽā§‡āĻ° āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ.

1. āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ

āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°āĻ¤ā§‡, āĻ†āĻŽāĻ°āĻž Node.js āĻ¨āĻžāĻŽā§‡ āĻāĻ•āĻŸāĻŋ āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ āĻ“āĻ¯āĻŧā§‡āĻŦ āĻĢā§āĻ°ā§‡āĻŽāĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻ• āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦ āĻĒā§āĻ°āĻ•āĻžāĻļ āĻ•āĻ°āĻž. āĻāĻŸāĻŋ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻĢāĻžāĻ‡āĻ˛ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ° āĻ•āĻ°āĻž āĻšāĻŦā§‡ src/server/server.js:

server.js āĻĒāĻžāĻ°ā§āĻŸ 1

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackConfig = require('../../webpack.dev.js');

// Setup an Express server
const app = express();
app.use(express.static('public'));

if (process.env.NODE_ENV === 'development') {
  // Setup Webpack for development
  const compiler = webpack(webpackConfig);
  app.use(webpackDevMiddleware(compiler));
} else {
  // Static serve the dist/ folder in production
  app.use(express.static('dist'));
}

// Listen on port
const port = process.env.PORT || 3000;
const server = app.listen(port);
console.log(`Server listening on port ${port}`);

āĻŽāĻ¨ā§‡ āĻ†āĻ›ā§‡ āĻĒā§āĻ°āĻĨāĻŽ āĻĒāĻ°ā§āĻŦā§‡ āĻ†āĻŽāĻ°āĻž Webpack āĻ¨āĻŋāĻ¯āĻŧā§‡ āĻ†āĻ˛ā§‹āĻšāĻ¨āĻž āĻ•āĻ°ā§‡āĻ›āĻŋ? āĻāĻ–āĻžāĻ¨ā§‡āĻ‡ āĻ†āĻŽāĻ°āĻž āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ“āĻ¯āĻŧā§‡āĻŦāĻĒā§āĻ¯āĻžāĻ• āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦāĨ¤ āĻ†āĻŽāĻ°āĻž āĻ¤āĻžāĻĻā§‡āĻ° āĻĻā§āĻŸāĻŋ āĻ‰āĻĒāĻžāĻ¯āĻŧā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦ:

  • āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ webpack-dev-midleware āĻ¸ā§āĻŦāĻ¯āĻŧāĻ‚āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻ­āĻžāĻŦā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ‰āĻ¨ā§āĻ¨āĻ¯āĻŧāĻ¨ āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻĒā§āĻ¨āĻ°ā§āĻ¨āĻŋāĻ°ā§āĻŽāĻžāĻŖ āĻ•āĻ°āĻ¤ā§‡, āĻŦāĻž
  • āĻ¸ā§āĻĨāĻŋāĻ°āĻ­āĻžāĻŦā§‡ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻ¸ā§āĻĨāĻžāĻ¨āĻžāĻ¨ā§āĻ¤āĻ° dist/, āĻ¯ā§‡āĻŸāĻŋāĻ¤ā§‡ Webpack āĻĒā§āĻ°ā§‹āĻĄāĻžāĻ•āĻļāĻ¨ āĻŦāĻŋāĻ˛ā§āĻĄ āĻ•āĻ°āĻžāĻ° āĻĒāĻ°ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋ āĻ˛āĻŋāĻ–āĻŦā§‡āĨ¤

āĻ†āĻ°ā§‡āĻ•āĻŸāĻŋ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻžāĻœ server.js āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ¸ā§‡āĻŸ āĻ†āĻĒ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻ¯āĻŧ socket.ioāĻ¯āĻž āĻļā§āĻ§ā§ āĻāĻ•ā§āĻ¸āĻĒā§āĻ°ā§‡āĻ¸ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ•āĻ°ā§‡:

server.js āĻĒāĻžāĻ°ā§āĻŸ 2

const socketio = require('socket.io');
const Constants = require('../shared/constants');

// Setup Express
// ...
const server = app.listen(port);
console.log(`Server listening on port ${port}`);

// Setup socket.io
const io = socketio(server);

// Listen for socket.io connections
io.on('connection', socket => {
  console.log('Player connected!', socket.id);

  socket.on(Constants.MSG_TYPES.JOIN_GAME, joinGame);
  socket.on(Constants.MSG_TYPES.INPUT, handleInput);
  socket.on('disconnect', onDisconnect);
});

āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻ¸āĻĢāĻ˛āĻ­āĻžāĻŦā§‡ āĻāĻ•āĻŸāĻŋ socket.io āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻžāĻ° āĻĒāĻ°, āĻ†āĻŽāĻ°āĻž āĻ¨āĻ¤ā§āĻ¨ āĻ¸āĻ•ā§‡āĻŸā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ° āĻ¸ā§‡āĻŸ āĻ†āĻĒ āĻ•āĻ°āĻŋāĨ¤ āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ°āĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻ¸āĻŋāĻ™ā§āĻ—āĻ˛āĻŸāĻ¨ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡ āĻ…āĻ°ā§āĻĒāĻŖ āĻ•āĻ°ā§‡ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸāĻĻā§‡āĻ° āĻ•āĻžāĻ› āĻĨā§‡āĻ•ā§‡ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻŦāĻžāĻ°ā§āĻ¤āĻžāĻ—ā§āĻ˛āĻŋ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§‡ game:

server.js āĻĒāĻžāĻ°ā§āĻŸ 3

const Game = require('./game');

// ...

// Setup the Game
const game = new Game();

function joinGame(username) {
  game.addPlayer(this, username);
}

function handleInput(dir) {
  game.handleInput(this, dir);
}

function onDisconnect() {
  game.removePlayer(this);
}

āĻ†āĻŽāĻ°āĻž āĻāĻ•āĻŸāĻŋ .io āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻ›āĻŋ, āĻ¤āĻžāĻ‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻāĻ•āĻŸāĻŋ āĻ•āĻĒāĻŋ āĻĻāĻ°āĻ•āĻžāĻ° Game ("āĻ—ā§‡āĻŽ") - āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧ āĻāĻ•āĻ‡ āĻ…āĻ™ā§āĻ—āĻ¨ā§‡ āĻ–ā§‡āĻ˛ā§‡! āĻĒāĻ°āĻŦāĻ°ā§āĻ¤ā§€ āĻŦāĻŋāĻ­āĻžāĻ—ā§‡, āĻ†āĻŽāĻ°āĻž āĻĻā§‡āĻ–āĻŦ āĻ•āĻŋāĻ­āĻžāĻŦā§‡ āĻāĻ‡ āĻ•ā§āĻ˛āĻžāĻ¸ āĻ•āĻžāĻœ āĻ•āĻ°ā§‡āĨ¤ Game.

2. āĻ—ā§‡āĻŽ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°

КĐģĐ°ŅŅ Game āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻĻāĻŋāĻ•ā§‡ āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ¯ā§āĻ•ā§āĻ¤āĻŋ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤ āĻāĻ° āĻĻā§āĻŸāĻŋ āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻ•āĻžāĻœ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡: āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧ āĻŦā§āĻ¯āĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž и āĻ—ā§‡āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§‡āĻļāĻ¨.

āĻĒā§āĻ°āĻĨāĻŽ āĻ•āĻžāĻœ, āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻŽā§āĻ¯āĻžāĻ¨ā§‡āĻœāĻŽā§‡āĻ¨ā§āĻŸ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻļā§āĻ°ā§ āĻ•āĻ°āĻž āĻ¯āĻžāĻ•āĨ¤

game.js āĻĒāĻžāĻ°ā§āĻŸ 1

const Constants = require('../shared/constants');
const Player = require('./player');

class Game {
  constructor() {
    this.sockets = {};
    this.players = {};
    this.bullets = [];
    this.lastUpdateTime = Date.now();
    this.shouldSendUpdate = false;
    setInterval(this.update.bind(this), 1000 / 60);
  }

  addPlayer(socket, username) {
    this.sockets[socket.id] = socket;

    // Generate a position to start this player at.
    const x = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
    const y = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
    this.players[socket.id] = new Player(socket.id, username, x, y);
  }

  removePlayer(socket) {
    delete this.sockets[socket.id];
    delete this.players[socket.id];
  }

  handleInput(socket, dir) {
    if (this.players[socket.id]) {
      this.players[socket.id].setDirection(dir);
    }
  }

  // ...
}

āĻāĻ‡ āĻ–ā§‡āĻ˛āĻžāĻ¯āĻŧ, āĻ†āĻŽāĻ°āĻž āĻŽāĻžāĻ ā§‡āĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°āĻŦ id āĻ¤āĻžāĻĻā§‡āĻ° socket.io āĻ¸āĻ•ā§‡āĻŸ (āĻ¯āĻĻāĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻŦāĻŋāĻ­ā§āĻ°āĻžāĻ¨ā§āĻ¤ āĻšāĻ¨, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻĢāĻŋāĻ°ā§‡ āĻ¯āĻžāĻ¨ server.js) Socket.io āĻ¨āĻŋāĻœā§‡āĻ‡ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ¸āĻ•ā§‡āĻŸāĻ•ā§‡ āĻāĻ•āĻŸāĻŋ āĻ…āĻ¨āĻ¨ā§āĻ¯ āĻŦāĻ°āĻžāĻĻā§āĻĻ āĻ•āĻ°ā§‡ idāĻ¤āĻžāĻ‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻāĻŸāĻž āĻ¨āĻŋāĻ¯āĻŧā§‡ āĻšāĻŋāĻ¨ā§āĻ¤āĻž āĻ•āĻ°āĻžāĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻ¨ā§‡āĻ‡āĨ¤ āĻ†āĻŽāĻŋ āĻ¤āĻžāĻ•ā§‡ āĻĄāĻžāĻ•āĻŦ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ†āĻ‡āĻĄāĻŋ.

āĻāĻŸāĻŋ āĻŽāĻžāĻĨāĻžāĻ¯āĻŧ āĻ°ā§‡āĻ–ā§‡, āĻ†āĻ¸ā§āĻ¨ āĻāĻ•āĻŸāĻŋ āĻ•ā§āĻ˛āĻžāĻ¸ā§‡ āĻ‡āĻ¨āĻ¸ā§āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻ­ā§‡āĻ°āĻŋāĻ¯āĻŧā§‡āĻŦāĻ˛āĻ—ā§āĻ˛āĻŋ āĻ…āĻ¨ā§āĻŦā§‡āĻˇāĻŖ āĻ•āĻ°āĻŋ Game:

  • sockets āĻāĻ•āĻŸāĻŋ āĻŦāĻ¸ā§āĻ¤ā§ āĻ¯āĻž āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ†āĻ‡āĻĄāĻŋāĻ•ā§‡ āĻ¸āĻ•ā§‡āĻŸā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§‡ āĻ¯āĻž āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§āĻ•ā§āĻ¤āĨ¤ āĻāĻŸāĻŋ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ§ā§āĻ°ā§āĻŦāĻ• āĻ¸āĻŽāĻ¯āĻŧā§‡ āĻ¤āĻžāĻĻā§‡āĻ° āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ†āĻ‡āĻĄāĻŋ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¸āĻ•ā§‡āĻŸ āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ āĻĻā§‡āĻ¯āĻŧāĨ¤
  • players āĻāĻ•āĻŸāĻŋ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ āĻ¯āĻž āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ†āĻ‡āĻĄāĻŋāĻ•ā§‡ āĻ•ā§‹āĻĄ>āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡ āĻ†āĻŦāĻĻā§āĻ§ āĻ•āĻ°ā§‡

bullets āĻŦāĻ¸ā§āĻ¤ā§āĻ° āĻāĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ°ā§‡ Bullet, āĻ¯āĻžāĻ° āĻ•ā§‹āĻ¨ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ†āĻĻā§‡āĻļ āĻ¨ā§‡āĻ‡āĨ¤
lastUpdateTime āĻ—ā§‡āĻŽāĻŸāĻŋ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°āĻž āĻļā§‡āĻˇ āĻ¸āĻŽāĻ¯āĻŧā§‡āĻ° āĻŸāĻžāĻ‡āĻŽāĻ¸ā§āĻŸā§āĻ¯āĻžāĻŽā§āĻĒāĨ¤ āĻ†āĻŽāĻ°āĻž āĻļā§€āĻ˜ā§āĻ°āĻ‡ āĻāĻŸāĻŋ āĻ•āĻŋāĻ­āĻžāĻŦā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻšāĻŦā§‡.
shouldSendUpdate āĻāĻ•āĻŸāĻŋ āĻ¸āĻšāĻžāĻ¯āĻŧāĻ• āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨āĻļā§€āĻ˛āĨ¤ āĻ†āĻŽāĻ°āĻž āĻ–ā§āĻŦ āĻļā§€āĻ˜ā§āĻ°āĻ‡ āĻāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ“ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦāĨ¤
āĻĒāĻĻā§āĻ§āĻ¤āĻŋ addPlayer(), removePlayer() и handleInput() āĻŦā§āĻ¯āĻžāĻ–ā§āĻ¯āĻž āĻ•āĻ°āĻžāĻ° āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ āĻ¨ā§‡āĻ‡, āĻ¤āĻžāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ server.js. āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¸ā§āĻŽā§ƒāĻ¤āĻŋ āĻ°āĻŋāĻĢā§āĻ°ā§‡āĻļ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨ āĻ¤āĻŦā§‡ āĻāĻ•āĻŸā§ āĻ‰āĻāĻšā§āĻ¤ā§‡ āĻĢāĻŋāĻ°ā§‡ āĻ¯āĻžāĻ¨āĨ¤

āĻļā§‡āĻˇ āĻ˛āĻžāĻ‡āĻ¨ constructor() āĻļā§āĻ°ā§ āĻ•āĻ°āĻž āĻ†āĻĒāĻĄā§‡āĻŸ āĻšāĻ•ā§āĻ° āĻ—ā§‡āĻŽāĻ¸ (60 āĻ†āĻĒāĻĄā§‡āĻŸ / āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡āĻ° āĻĢā§āĻ°āĻŋāĻ•ā§‹āĻ¯āĻŧā§‡āĻ¨ā§āĻ¸āĻŋ āĻ¸āĻš):

game.js āĻĒāĻžāĻ°ā§āĻŸ 2

const Constants = require('../shared/constants');
const applyCollisions = require('./collisions');

class Game {
  // ...

  update() {
    // Calculate time elapsed
    const now = Date.now();
    const dt = (now - this.lastUpdateTime) / 1000;
    this.lastUpdateTime = now;

    // Update each bullet
    const bulletsToRemove = [];
    this.bullets.forEach(bullet => {
      if (bullet.update(dt)) {
        // Destroy this bullet
        bulletsToRemove.push(bullet);
      }
    });
    this.bullets = this.bullets.filter(
      bullet => !bulletsToRemove.includes(bullet),
    );

    // Update each player
    Object.keys(this.sockets).forEach(playerID => {
      const player = this.players[playerID];
      const newBullet = player.update(dt);
      if (newBullet) {
        this.bullets.push(newBullet);
      }
    });

    // Apply collisions, give players score for hitting bullets
    const destroyedBullets = applyCollisions(
      Object.values(this.players),
      this.bullets,
    );
    destroyedBullets.forEach(b => {
      if (this.players[b.parentID]) {
        this.players[b.parentID].onDealtDamage();
      }
    });
    this.bullets = this.bullets.filter(
      bullet => !destroyedBullets.includes(bullet),
    );

    // Check if any players are dead
    Object.keys(this.sockets).forEach(playerID => {
      const socket = this.sockets[playerID];
      const player = this.players[playerID];
      if (player.hp <= 0) {
        socket.emit(Constants.MSG_TYPES.GAME_OVER);
        this.removePlayer(socket);
      }
    });

    // Send a game update to each player every other time
    if (this.shouldSendUpdate) {
      const leaderboard = this.getLeaderboard();
      Object.keys(this.sockets).forEach(playerID => {
        const socket = this.sockets[playerID];
        const player = this.players[playerID];
        socket.emit(
          Constants.MSG_TYPES.GAME_UPDATE,
          this.createUpdate(player, leaderboard),
        );
      });
      this.shouldSendUpdate = false;
    } else {
      this.shouldSendUpdate = true;
    }
  }

  // ...
}

āĻĒāĻĻā§āĻ§āĻ¤āĻŋ update() āĻ¸āĻŽā§āĻ­āĻŦāĻ¤ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°-āĻ¸āĻžāĻ‡āĻĄ āĻ˛āĻœāĻŋāĻ•ā§‡āĻ° āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻ…āĻ‚āĻļ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤ āĻ•ā§āĻ°āĻŽāĻžāĻ¨ā§āĻ¸āĻžāĻ°ā§‡ āĻāĻŸāĻŋ āĻ¯āĻž āĻ•āĻ°ā§‡ āĻ¤āĻž āĻāĻ–āĻžāĻ¨ā§‡:

  1. āĻ•āĻ¤āĻ•ā§āĻˇāĻŖ āĻšāĻŋāĻ¸āĻžāĻŦ āĻ•āĻ°ā§‡ dt āĻļā§‡āĻˇ āĻĨā§‡āĻ•ā§‡ āĻĒāĻžāĻ¸ update().
  2. āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻ°āĻŋāĻĢā§āĻ°ā§‡āĻļ āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§‡ āĻ¤āĻžāĻĻā§‡āĻ° āĻ§ā§āĻŦāĻ‚āĻ¸ āĻ•āĻ°ā§‡āĨ¤ āĻ†āĻŽāĻ°āĻž āĻĒāĻ°ā§‡ āĻāĻ‡ āĻ•āĻžāĻ°ā§āĻ¯āĻ•āĻžāĻ°āĻŋāĻ¤āĻž āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻšāĻŦā§‡. āĻ†āĻĒāĻžāĻ¤āĻ¤ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸ā§‡āĻŸāĻž āĻœāĻžāĻ¨āĻžāĻ‡ āĻ¯āĻĨā§‡āĻˇā§āĻŸ bullet.update() āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻŦāĻ°ā§āĻ¤āĻ¨ trueāĻ¯āĻĻāĻŋ āĻĒā§āĻ°āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤ āĻ§ā§āĻŦāĻ‚āĻ¸ āĻ•āĻ°āĻž āĻ‰āĻšāĻŋāĻ¤ (āĻ¤āĻŋāĻ¨āĻŋ āĻŽāĻ¯āĻŧāĻĻāĻžāĻ¨ āĻĨā§‡āĻ•ā§‡ āĻŦā§‡āĻ°āĻŋāĻ¯āĻŧā§‡ āĻ†āĻ¸ā§‡āĻ¨)āĨ¤
  3. āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻ•ā§‡ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§‡āĨ¤ āĻ†āĻŽāĻ°āĻž āĻĒāĻ°ā§‡ āĻāĻ‡ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦ- player.update() āĻāĻ•āĻŸāĻŋ āĻŦāĻ¸ā§āĻ¤ā§ āĻĢā§‡āĻ°āĻ¤ āĻĻāĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ Bullet.
  4. āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻāĻŦāĻ‚ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻĻā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻž āĻ•āĻ°ā§‡ applyCollisions(), āĻ¯āĻž āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ†āĻ˜āĻžāĻ¤āĻ•āĻžāĻ°ā§€ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ°ā§‡ āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻ•āĻ°ā§‡āĨ¤ āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡āĻ° āĻœāĻ¨ā§āĻ¯, āĻ¯ā§‡ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻŸāĻŋ āĻāĻŸāĻŋāĻ•ā§‡ āĻ—ā§āĻ˛āĻŋ āĻ•āĻ°ā§‡āĻ›ā§‡ āĻ†āĻŽāĻ°āĻž āĻ¤āĻžāĻ° āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻŦāĻžāĻĄāĻŧāĻžāĻ‡ (āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ player.onDealtDamage()) āĻāĻŦāĻ‚ āĻ¤āĻžāĻ°āĻĒāĻ° āĻ…ā§āĻ¯āĻžāĻ°ā§‡ āĻĨā§‡āĻ•ā§‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛āĻŸāĻŋ āĻ¸āĻ°āĻžāĻ¨ bullets.
  5. āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¨āĻŋāĻšāĻ¤ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻ•ā§‡ āĻ…āĻŦāĻšāĻŋāĻ¤ āĻ•āĻ°ā§‡ āĻāĻŦāĻ‚ āĻ§ā§āĻŦāĻ‚āĻ¸ āĻ•āĻ°ā§‡āĨ¤
  6. āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ āĻžāĻ¯āĻŧ āĻĒā§āĻ°āĻ¤āĻŋ āĻŽā§āĻšā§‚āĻ°ā§āĻ¤ āĻŦāĻžāĻ° āĻ¯āĻ–āĻ¨ āĻĄāĻžāĻ•āĻž āĻšāĻ¯āĻŧ update(). āĻāĻŸāĻŋ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ‰āĻĒāĻ°ā§‡ āĻ‰āĻ˛ā§āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻ…āĻ•ā§āĻœāĻŋāĻ˛āĻŋāĻ¯āĻŧāĻžāĻ°ā§€ āĻ­ā§‡āĻ°āĻŋāĻ¯āĻŧā§‡āĻŦāĻ˛ā§‡āĻ° āĻŸā§āĻ°ā§āĻ¯āĻžāĻ• āĻ°āĻžāĻ–āĻ¤ā§‡ āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ āĻ•āĻ°ā§‡āĨ¤ shouldSendUpdate. āĻšāĻŋāĻ¸āĻžāĻŦā§‡ update() 60 āĻŦāĻžāĻ°/āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ, āĻ†āĻŽāĻ°āĻž 30 āĻŦāĻžāĻ°/āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ āĻžāĻ‡āĨ¤ āĻāĻ‡āĻ­āĻžāĻŦā§‡, āĻ˜āĻĄāĻŧāĻŋ āĻĢā§āĻ°āĻŋāĻ•ā§‹āĻ¯āĻŧā§‡āĻ¨ā§āĻ¸āĻŋ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ˜āĻĄāĻŧāĻŋ āĻšāĻ˛ 30 āĻ˜āĻĄāĻŧāĻŋ/āĻ¸ā§‡ (āĻ†āĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻ…āĻ‚āĻļā§‡ āĻ˜āĻĄāĻŧāĻŋāĻ° āĻšāĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ•āĻĨāĻž āĻŦāĻ˛ā§‡āĻ›āĻŋ)āĨ¤

āĻ•ā§‡āĻ¨ āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻĒāĻžāĻ āĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ ? āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻ¤ā§‡āĨ¤ āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ ā§Šā§ĻāĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ…āĻ¨ā§‡āĻ•!

āĻļā§āĻ§ā§ āĻĄāĻžāĻ•āĻŦā§‡ āĻ¨āĻž āĻ•ā§‡āĻ¨ update() āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ ā§Šā§Ļ āĻŦāĻžāĻ°? āĻ—ā§‡āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§‡āĻļāĻ¨ āĻ‰āĻ¨ā§āĻ¨āĻ¤ āĻ•āĻ°āĻ¤ā§‡. āĻ†āĻ°ā§‹ āĻĒā§āĻ°āĻžāĻ¯āĻŧāĻ‡ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ update(), āĻ—ā§‡āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§‡āĻļāĻ¨ āĻ†āĻ°ā§‹ āĻ¨āĻŋāĻ°ā§āĻ­ā§āĻ˛ āĻšāĻŦā§‡. āĻ¤āĻŦā§‡ āĻšā§āĻ¯āĻžāĻ˛ā§‡āĻžā§āĻœā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž āĻ¨āĻŋāĻ¯āĻŧā§‡ āĻ–ā§āĻŦ āĻŦā§‡āĻļāĻŋ āĻĻā§‚āĻ°ā§‡ āĻšāĻ˛ā§‡ āĻ¯āĻžāĻŦā§‡āĻ¨ āĻ¨āĻžāĨ¤ update(), āĻ•āĻžāĻ°āĻŖ āĻāĻŸāĻŋ āĻāĻ•āĻŸāĻŋ āĻ—āĻŖāĻ¨āĻžāĻ—āĻ¤āĻ­āĻžāĻŦā§‡ āĻŦā§āĻ¯āĻ¯āĻŧāĻŦāĻšā§āĻ˛ āĻ•āĻžāĻœ - āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ 60 āĻ¯āĻĨā§‡āĻˇā§āĻŸāĨ¤

āĻŦāĻžāĻ•āĻŋ āĻ•ā§āĻ˛āĻžāĻ¸ Game āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯āĻ•āĻžāĻ°ā§€ āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ update():

game.js āĻĒāĻžāĻ°ā§āĻŸ 3

class Game {
  // ...

  getLeaderboard() {
    return Object.values(this.players)
      .sort((p1, p2) => p2.score - p1.score)
      .slice(0, 5)
      .map(p => ({ username: p.username, score: Math.round(p.score) }));
  }

  createUpdate(player, leaderboard) {
    const nearbyPlayers = Object.values(this.players).filter(
      p => p !== player && p.distanceTo(player) <= Constants.MAP_SIZE / 2,
    );
    const nearbyBullets = this.bullets.filter(
      b => b.distanceTo(player) <= Constants.MAP_SIZE / 2,
    );

    return {
      t: Date.now(),
      me: player.serializeForUpdate(),
      others: nearbyPlayers.map(p => p.serializeForUpdate()),
      bullets: nearbyBullets.map(b => b.serializeForUpdate()),
      leaderboard,
    };
  }
}

getLeaderboard() āĻŦā§‡āĻļ āĻ¸āĻšāĻœ - āĻāĻŸāĻŋ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ¸ā§āĻ•ā§‹āĻ° āĻ…āĻ¨ā§āĻ¸āĻžāĻ°ā§‡ āĻ¸āĻžāĻœāĻžāĻ¯āĻŧ, āĻļā§€āĻ°ā§āĻˇ āĻĒāĻžāĻāĻšāĻŸāĻŋ āĻ¨ā§‡āĻ¯āĻŧ āĻāĻŦāĻ‚ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āĻ•ā§‹āĻ° āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻ•āĻ°ā§‡āĨ¤

createUpdate() āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ update() āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻ¤ā§‡ āĻ¯āĻž āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĨ¤ āĻāĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻ•āĻžāĻœ āĻšāĻ˛ āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻ•āĻ˛ āĻ•āĻ°āĻž serializeForUpdate()āĻ•ā§āĻ˛āĻžāĻ¸ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻŋāĻ¤ Player и Bullet. āĻŽāĻ¨ā§‡ āĻ°āĻžāĻ–āĻŦā§‡āĻ¨ āĻ¯ā§‡ āĻāĻŸāĻŋ āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧā§‡āĻ° āĻ•āĻžāĻ›ā§‡ āĻĄā§‡āĻŸāĻž āĻĒāĻžāĻ¸ āĻ•āĻ°ā§‡ āĻ¨āĻŋāĻ•āĻŸāĻ¤āĻŽ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻāĻŦāĻ‚ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ - āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻĻā§‚āĻ°ā§‡ āĻĨāĻžāĻ•āĻž āĻ—ā§‡āĻŽ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ¤āĻĨā§āĻ¯ āĻĒā§āĻ°ā§‡āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻ¨ā§‡āĻ‡!

3. āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻ—ā§‡āĻŽ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ—ā§‡āĻŽā§‡, āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻāĻŦāĻ‚ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻ—ā§āĻ˛āĻŋ āĻ†āĻ¸āĻ˛ā§‡ āĻ–ā§āĻŦ āĻāĻ•āĻ‡ āĻ°āĻ•āĻŽ: āĻ¤āĻžāĻ°āĻž āĻŦāĻŋāĻŽā§‚āĻ°ā§āĻ¤, āĻ—ā§‹āĻ˛āĻžāĻ•āĻžāĻ°, āĻšāĻ˛āĻŽāĻžāĻ¨ āĻ—ā§‡āĻŽ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸāĨ¤ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻāĻŦāĻ‚ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻāĻ‡ āĻ¸āĻžāĻĻā§ƒāĻļā§āĻ¯ā§‡āĻ° āĻ¸ā§āĻŦāĻŋāĻ§āĻž āĻ¨āĻŋāĻ¤ā§‡, āĻŦā§‡āĻ¸ āĻ•ā§āĻ˛āĻžāĻ¸ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ā§‡āĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻļā§āĻ°ā§ āĻ•āĻ°āĻž āĻ¯āĻžāĻ• Object:

object.js

class Object {
  constructor(id, x, y, dir, speed) {
    this.id = id;
    this.x = x;
    this.y = y;
    this.direction = dir;
    this.speed = speed;
  }

  update(dt) {
    this.x += dt * this.speed * Math.sin(this.direction);
    this.y -= dt * this.speed * Math.cos(this.direction);
  }

  distanceTo(object) {
    const dx = this.x - object.x;
    const dy = this.y - object.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  setDirection(dir) {
    this.direction = dir;
  }

  serializeForUpdate() {
    return {
      id: this.id,
      x: this.x,
      y: this.y,
    };
  }
}

āĻāĻ–āĻžāĻ¨ā§‡ āĻœāĻŸāĻŋāĻ˛ āĻ•āĻŋāĻ›ā§ āĻšāĻšā§āĻ›ā§‡ āĻ¨āĻžāĨ¤ āĻāĻ‡ āĻ•ā§āĻ˛āĻžāĻ¸āĻŸāĻŋ āĻāĻ•ā§āĻ¸āĻŸā§‡āĻ¨āĻļāĻ¨ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻ•āĻŸāĻŋ āĻ­āĻžāĻ˛ āĻ…ā§āĻ¯āĻžāĻ™ā§āĻ•āĻ° āĻĒāĻ¯āĻŧā§‡āĻ¨ā§āĻŸ āĻšāĻŦā§‡āĨ¤ āĻĻā§‡āĻ–āĻŋ āĻ•ā§āĻ˛āĻžāĻ¸ āĻ•ā§‡āĻŽāĻ¨ āĻšāĻ¯āĻŧ Bullet āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ¸āĻŽā§‚āĻš Object:

āĻŦā§āĻ˛ā§‡āĻŸ.āĻœā§‡āĻāĻ¸

const shortid = require('shortid');
const ObjectClass = require('./object');
const Constants = require('../shared/constants');

class Bullet extends ObjectClass {
  constructor(parentID, x, y, dir) {
    super(shortid(), x, y, dir, Constants.BULLET_SPEED);
    this.parentID = parentID;
  }

  // Returns true if the bullet should be destroyed
  update(dt) {
    super.update(dt);
    return this.x < 0 || this.x > Constants.MAP_SIZE || this.y < 0 || this.y > Constants.MAP_SIZE;
  }
}

āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ Bullet āĻ–ā§āĻŦ āĻ›ā§‹āĻŸ! āĻ†āĻŽāĻ°āĻž āĻ¯ā§‹āĻ— āĻ•āĻ°ā§‡āĻ›āĻŋ Object āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻāĻ•ā§āĻ¸āĻŸā§‡āĻ¨āĻļāĻ¨:

  • āĻĒā§āĻ¯āĻžāĻ•ā§‡āĻœ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻ¸āĻ‚āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤ āĻāĻ˛ā§‹āĻŽā§‡āĻ˛ā§‹ āĻĒā§āĻ°āĻœāĻ¨ā§āĻŽā§‡āĻ° āĻœāĻ¨ā§āĻ¯ id āĻĒā§āĻ°āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤
  • āĻāĻ•āĻŸāĻŋ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ° āĻ¯ā§‹āĻ— āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡ parentIDāĻ¯āĻžāĻ¤ā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻāĻ‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻ•ā§‡ āĻŸā§āĻ°ā§āĻ¯āĻžāĻ• āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤
  • āĻāĻ•āĻŸāĻŋ āĻ°āĻŋāĻŸāĻžāĻ°ā§āĻ¨ āĻŽāĻžāĻ¨ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡ update()āĻ¯āĻž āĻ¸āĻŽāĻžāĻ¨ trueāĻ¯āĻĻāĻŋ āĻĒā§āĻ°āĻ•ā§āĻˇāĻŋāĻĒā§āĻ¤āĻŸāĻŋ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°āĻŸāĻŋāĻ° āĻŦāĻžāĻ‡āĻ°ā§‡ āĻĨāĻžāĻ•ā§‡ (āĻŽāĻ¨ā§‡ āĻ†āĻ›ā§‡ āĻ†āĻŽāĻ°āĻž āĻļā§‡āĻˇ āĻŦāĻŋāĻ­āĻžāĻ—ā§‡ āĻāĻŸāĻŋ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ•āĻĨāĻž āĻŦāĻ˛ā§‡āĻ›āĻŋ?)

āĻšāĻ˛ā§āĻ¨ āĻšāĻ˛ā§āĻ¨ Player:

player.js

const ObjectClass = require('./object');
const Bullet = require('./bullet');
const Constants = require('../shared/constants');

class Player extends ObjectClass {
  constructor(id, username, x, y) {
    super(id, x, y, Math.random() * 2 * Math.PI, Constants.PLAYER_SPEED);
    this.username = username;
    this.hp = Constants.PLAYER_MAX_HP;
    this.fireCooldown = 0;
    this.score = 0;
  }

  // Returns a newly created bullet, or null.
  update(dt) {
    super.update(dt);

    // Update score
    this.score += dt * Constants.SCORE_PER_SECOND;

    // Make sure the player stays in bounds
    this.x = Math.max(0, Math.min(Constants.MAP_SIZE, this.x));
    this.y = Math.max(0, Math.min(Constants.MAP_SIZE, this.y));

    // Fire a bullet, if needed
    this.fireCooldown -= dt;
    if (this.fireCooldown <= 0) {
      this.fireCooldown += Constants.PLAYER_FIRE_COOLDOWN;
      return new Bullet(this.id, this.x, this.y, this.direction);
    }
    return null;
  }

  takeBulletDamage() {
    this.hp -= Constants.BULLET_DAMAGE;
  }

  onDealtDamage() {
    this.score += Constants.SCORE_BULLET_HIT;
  }

  serializeForUpdate() {
    return {
      ...(super.serializeForUpdate()),
      direction: this.direction,
      hp: this.hp,
    };
  }
}

āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻ—ā§āĻ˛āĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡āĻ° āĻšā§‡āĻ¯āĻŧā§‡ āĻŦā§‡āĻļāĻŋ āĻœāĻŸāĻŋāĻ˛, āĻ¤āĻžāĻ‡ āĻāĻ‡ āĻ•ā§āĻ˛āĻžāĻ¸ā§‡ āĻ†āĻ°āĻ“ āĻ•āĻ¯āĻŧā§‡āĻ•āĻŸāĻŋ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ° āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻž āĻ‰āĻšāĻŋāĻ¤āĨ¤ āĻ¤āĻžāĻ° āĻĒāĻĻā§āĻ§āĻ¤āĻŋ update() āĻ…āĻ¨ā§‡āĻ• āĻ•āĻžāĻœ āĻ•āĻ°ā§‡, āĻŦāĻŋāĻļā§‡āĻˇ āĻ•āĻ°ā§‡, āĻ¯āĻĻāĻŋ āĻ•āĻŋāĻ›ā§ āĻ…āĻŦāĻļāĻŋāĻˇā§āĻŸ āĻ¨āĻž āĻĨāĻžāĻ•ā§‡ āĻ¤āĻŦā§‡ āĻ¨āĻ¤ā§āĻ¨ āĻ¤ā§ˆāĻ°āĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻĢā§‡āĻ°āĻ¤ āĻĻā§‡āĻ¯āĻŧ fireCooldown (āĻŽāĻ¨ā§‡ āĻ†āĻ›ā§‡ āĻ†āĻŽāĻ°āĻž āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āĻ¤ā§€ āĻŦāĻŋāĻ­āĻžāĻ—ā§‡ āĻāĻ‡ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ•āĻĨāĻž āĻŦāĻ˛ā§‡āĻ›āĻŋ?) āĻāĻ›āĻžāĻĄāĻŧāĻžāĻ“ āĻāĻŸāĻŋ āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻĒā§āĻ°āĻ¸āĻžāĻ°āĻŋāĻ¤ serializeForUpdate(), āĻ•āĻžāĻ°āĻŖ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ—ā§‡āĻŽ āĻ†āĻĒāĻĄā§‡āĻŸā§‡ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ° āĻ…āĻ¨ā§āĻ¤āĻ°ā§āĻ­ā§āĻ•ā§āĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻŦā§‡āĨ¤

āĻāĻ•āĻŸāĻŋ āĻŦā§‡āĻ¸ āĻ•ā§āĻ˛āĻžāĻ¸ āĻšāĻšā§āĻ›ā§‡ Object - āĻ•ā§‹āĻĄ āĻĒā§āĻ¨āĻ°āĻžāĻŦā§ƒāĻ¤ā§āĻ¤āĻŋ āĻāĻĄāĻŧāĻžāĻ¤ā§‡ āĻāĻ•āĻŸāĻŋ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻĻāĻ•ā§āĻˇā§‡āĻĒ. āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻ¸ā§āĻŦāĻ°ā§‚āĻĒ, āĻ•ā§‹āĻ¨ āĻ•ā§āĻ˛āĻžāĻ¸ āĻ¨ā§‡āĻ‡ Object āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ—ā§‡āĻŽ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ āĻāĻ•āĻ‡ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĨāĻžāĻ•āĻ¤ā§‡ āĻšāĻŦā§‡ distanceTo(), āĻāĻŦāĻ‚ āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻĢāĻžāĻ‡āĻ˛ āĻœā§āĻĄāĻŧā§‡ āĻāĻ‡ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•āĻĒāĻŋ-āĻĒā§‡āĻ¸ā§āĻŸ āĻ•āĻ°āĻž āĻāĻ•āĻŸāĻŋ āĻĻā§āĻƒāĻ¸ā§āĻŦāĻĒā§āĻ¨ āĻšāĻŦā§‡āĨ¤ āĻāĻŸāĻŋ āĻŦāĻĄāĻŧ āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻŦāĻŋāĻļā§‡āĻˇāĻ­āĻžāĻŦā§‡ āĻ—ā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ¯āĻŧā§‡ āĻ“āĻ ā§‡āĨ¤āĻ¯āĻ–āĻ¨ āĻĒā§āĻ°āĻ¸āĻžāĻ°āĻŋāĻ¤ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž Object āĻ•ā§āĻ˛āĻžāĻ¸ āĻŦāĻžāĻĄāĻŧāĻ›ā§‡āĨ¤

4. āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇ āĻ¸āĻ¨āĻžāĻ•ā§āĻ¤āĻ•āĻ°āĻŖ

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻ•āĻŽāĻžāĻ¤ā§āĻ° āĻœāĻŋāĻ¨āĻŋāĻ¸ āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡ āĻ¯āĻ–āĻ¨ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ†āĻ˜āĻžāĻ¤! āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻĨā§‡āĻ•ā§‡ āĻ•ā§‹āĻĄ āĻāĻ‡ āĻŸā§āĻ•āĻ°āĻž āĻŽāĻ¨ā§‡ āĻ°āĻžāĻ–āĻŦā§‡āĻ¨ update() āĻ•ā§āĻ˛āĻžāĻ¸ā§‡ Game:

game.js

const applyCollisions = require('./collisions');

class Game {
  // ...

  update() {
    // ...

    // Apply collisions, give players score for hitting bullets
    const destroyedBullets = applyCollisions(
      Object.values(this.players),
      this.bullets,
    );
    destroyedBullets.forEach(b => {
      if (this.players[b.parentID]) {
        this.players[b.parentID].onDealtDamage();
      }
    });
    this.bullets = this.bullets.filter(
      bullet => !destroyedBullets.includes(bullet),
    );

    // ...
  }
}

āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻĒāĻĻā§āĻ§āĻ¤āĻŋāĻŸāĻŋ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻŦā§‡ applyCollisions(), āĻ¯āĻž āĻ–ā§‡āĻ˛ā§‹āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§‡āĻ° āĻ†āĻ˜āĻžāĻ¤āĻ•āĻžāĻ°ā§€ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ āĻĢā§‡āĻ°āĻ¤ āĻĻā§‡āĻ¯āĻŧāĨ¤ āĻ­āĻžāĻ—ā§āĻ¯āĻ•ā§āĻ°āĻŽā§‡, āĻāĻŸāĻž āĻ•āĻ°āĻž āĻ•āĻ āĻŋāĻ¨ āĻ¨āĻ¯āĻŧ āĻ•āĻžāĻ°āĻŖ

  • āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇāĻ•āĻžāĻ°ā§€ āĻŦāĻ¸ā§āĻ¤ā§ āĻŦā§ƒāĻ¤ā§āĻ¤, āĻāĻŦāĻ‚ āĻāĻŸāĻŋ āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇ āĻ¸āĻ¨āĻžāĻ•ā§āĻ¤āĻ•āĻ°āĻŖ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸āĻŦāĻšā§‡āĻ¯āĻŧā§‡ āĻ¸āĻšāĻœ āĻ†āĻ•ā§ƒāĻ¤āĻŋāĨ¤
  • āĻ†āĻŽāĻ°āĻž āĻ‡āĻ¤āĻŋāĻŽāĻ§ā§āĻ¯ā§‡ āĻāĻ•āĻŸāĻŋ āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻ†āĻ›ā§‡ distanceTo(), āĻ¯āĻž āĻ†āĻŽāĻ°āĻž āĻ•ā§āĻ˛āĻžāĻ¸ā§‡ āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āĻ¤ā§€ āĻŦāĻŋāĻ­āĻžāĻ—ā§‡ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻ— āĻ•āĻ°ā§‡āĻ›āĻŋ Object.

āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇ āĻļāĻ¨āĻžāĻ•ā§āĻ¤āĻ•āĻ°āĻŖā§‡āĻ° āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ•ā§‡āĻŽāĻ¨ āĻĻā§‡āĻ–āĻžāĻ¯āĻŧ āĻ¤āĻž āĻāĻ–āĻžāĻ¨ā§‡:

collisions.js

const Constants = require('../shared/constants');

// Returns an array of bullets to be destroyed.
function applyCollisions(players, bullets) {
  const destroyedBullets = [];
  for (let i = 0; i < bullets.length; i++) {
    // Look for a player (who didn't create the bullet) to collide each bullet with.
    // As soon as we find one, break out of the loop to prevent double counting a bullet.
    for (let j = 0; j < players.length; j++) {
      const bullet = bullets[i];
      const player = players[j];
      if (
        bullet.parentID !== player.id &&
        player.distanceTo(bullet) <= Constants.PLAYER_RADIUS + Constants.BULLET_RADIUS
      ) {
        destroyedBullets.push(bullet);
        player.takeBulletDamage();
        break;
      }
    }
  }
  return destroyedBullets;
}

āĻāĻ‡ āĻ¸āĻšāĻœ āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇ āĻ¸āĻ¨āĻžāĻ•ā§āĻ¤āĻ•āĻ°āĻŖ āĻ¸āĻ¤ā§āĻ¯ āĻ¯ā§‡ āĻ‰āĻĒāĻ° āĻ­āĻŋāĻ¤ā§āĻ¤āĻŋ āĻ•āĻ°ā§‡ āĻĻā§āĻŸāĻŋ āĻŦā§ƒāĻ¤ā§āĻ¤ā§‡āĻ° āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇ āĻšāĻ¯āĻŧ āĻ¯āĻĻāĻŋ āĻ¤āĻžāĻĻā§‡āĻ° āĻ•ā§‡āĻ¨ā§āĻĻā§āĻ°ā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻĻā§‚āĻ°āĻ¤ā§āĻŦ āĻ¤āĻžāĻĻā§‡āĻ° āĻŦā§āĻ¯āĻžāĻ¸āĻžāĻ°ā§āĻ§ā§‡āĻ° āĻ¯ā§‹āĻ—āĻĢāĻ˛ā§‡āĻ° āĻšā§‡āĻ¯āĻŧā§‡ āĻ•āĻŽ āĻšāĻ¯āĻŧ. āĻāĻ–āĻžāĻ¨ā§‡ āĻĻā§āĻŸāĻŋ āĻŦā§ƒāĻ¤ā§āĻ¤ā§‡āĻ° āĻ•ā§‡āĻ¨ā§āĻĻā§āĻ°ā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻĻā§‚āĻ°āĻ¤ā§āĻŦ āĻ¤āĻžāĻĻā§‡āĻ° āĻŦā§āĻ¯āĻžāĻ¸āĻžāĻ°ā§āĻ§ā§‡āĻ° āĻ¸āĻŽāĻˇā§āĻŸāĻŋāĻ° āĻ¸āĻŽāĻžāĻ¨:

āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ˛ā§āĻŸāĻŋāĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž
āĻāĻ–āĻžāĻ¨ā§‡ āĻŦāĻŋāĻŦā§‡āĻšāĻ¨āĻž āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻ°āĻ“ āĻ•āĻ¯āĻŧā§‡āĻ•āĻŸāĻŋ āĻĻāĻŋāĻ• āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡:

  • āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛āĻŸāĻŋ āĻ¯ā§‡ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°āĻŸāĻŋ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§‡āĻ›ā§‡ āĻ¤āĻžāĻ•ā§‡ āĻ†āĻ˜āĻžāĻ¤ āĻ•āĻ°āĻž āĻ‰āĻšāĻŋāĻ¤ āĻ¨āĻ¯āĻŧāĨ¤ āĻāĻŸāĻŋ āĻ¤ā§āĻ˛āĻ¨āĻž āĻ•āĻ°ā§‡ āĻ…āĻ°ā§āĻœāĻ¨ āĻ•āĻ°āĻž āĻ¯ā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡ bullet.parentID Ņ player.id.
  • āĻāĻ•āĻ‡ āĻ¸āĻŽāĻ¯āĻŧā§‡ āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ°ā§‡āĻ° āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇā§‡āĻ° āĻ¸ā§€āĻŽāĻŋāĻ¤ āĻ•ā§āĻˇā§‡āĻ¤ā§āĻ°ā§‡ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛āĻ•ā§‡ āĻ…āĻŦāĻļā§āĻ¯āĻ‡ āĻāĻ•āĻŦāĻžāĻ° āĻ†āĻ˜āĻžāĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻŦā§‡āĨ¤ āĻ†āĻŽāĻ°āĻž āĻ…āĻĒāĻžāĻ°ā§‡āĻŸāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻāĻ‡ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻ° āĻ¸āĻŽāĻžāĻ§āĻžāĻ¨ āĻ•āĻ°āĻŦ break: āĻ¯āĻ¤ āĻ¤āĻžāĻĄāĻŧāĻžāĻ¤āĻžāĻĄāĻŧāĻŋ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ˜āĻ°ā§āĻˇāĻ•āĻžāĻ°ā§€ āĻĒā§āĻ˛ā§‡āĻ¯āĻŧāĻžāĻ° āĻĒāĻžāĻ“āĻ¯āĻŧāĻž āĻ¯āĻžāĻ¯āĻŧ, āĻ†āĻŽāĻ°āĻž āĻ…āĻ¨ā§āĻ¸āĻ¨ā§āĻ§āĻžāĻ¨ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻŋ āĻāĻŦāĻ‚ āĻĒāĻ°āĻŦāĻ°ā§āĻ¤ā§€ āĻĒā§āĻ°āĻœā§‡āĻ•ā§āĻŸāĻžāĻ‡āĻ˛ā§‡ āĻšāĻ˛ā§‡ āĻ¯āĻžāĻ‡āĨ¤

āĻļā§‡āĻˇ

āĻāĻ–āĻžāĻ¨ā§‡āĻ‡ āĻļā§‡āĻˇ! āĻāĻ•āĻŸāĻŋ .io āĻ“āĻ¯āĻŧā§‡āĻŦ āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¯āĻž āĻœāĻžāĻ¨āĻž āĻĻāĻ°āĻ•āĻžāĻ° āĻ¤āĻž āĻ†āĻŽāĻ°āĻž āĻ•āĻ­āĻžāĻ° āĻ•āĻ°ā§‡āĻ›āĻŋāĨ¤ āĻāĻ°āĻĒāĻ° āĻ•āĻŋ? āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨āĻŋāĻœāĻ¸ā§āĻŦ .io āĻ—ā§‡āĻŽ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨!

āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¨āĻŽā§āĻ¨āĻž āĻ•ā§‹āĻĄ āĻ“āĻĒā§‡āĻ¨ āĻ¸ā§‹āĻ°ā§āĻ¸ āĻāĻŦāĻ‚ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡ āĻ—āĻŋāĻŸāĻšāĻžāĻŦ.

āĻ‰āĻ¤ā§āĻ¸: www.habr.com

āĻāĻ•āĻŸāĻŋ āĻŽāĻ¨ā§āĻ¤āĻŦā§āĻ¯ āĻœā§āĻĄāĻŧā§āĻ¨