แแแแแแแแ 2015 แฌแแแก
แแฃ แแฅแแแแ แแ แแกแแแแก แแกแแแแแแ แแ แแแแแจแแแแก แจแแกแแฎแแ, แแก แแ แแก แฃแคแแกแ แแ แแแแแแแแแแแจแแแแ แแแ แแแแแจแแแ, แ แแแแแแ แแแแแจแ แแแ แขแแแแ (แแ แแ แแก แกแแญแแ แ แแแแแ แแจแ). แ แแแแ แช แฌแแกแ, แแกแแแ แแ แแกแ แแ แแแแแ แแ แแแแแ แแแแ แแแฌแแแแแฆแแแแแ แแแแแแแจแแก แฎแแแแแแแ. แกแฎแแ แชแแแแแแ .io แแแแแจแแแ:
แแ แแแกแขแจแ แฉแแแ แแแแแ แแแแแ แ แแแแ แจแแฅแแแแแ .io แแแแแจแ แแฃแแแแแ. แแแแกแแแแแก แกแแแแแ แแกแ แแฅแแแแ แแฎแแแแ Javascript-แแก แชแแแแ: แแฅแแแ แฃแแแ แแแกแแแแแ แแกแแแ แ แแฆแแชแแแแ, แ แแแแ แแชแแ แกแแแขแแฅแกแ this
ะธ
.io แแแแแจแแก แแแแแแแแ
แกแแกแฌแแแแ แแแฎแแแ แแแแกแแแแก แแแแแแ แแแแ
แแแแแจแ แกแแแแแแ แแแ แขแแแแ: แแฅแแแ แแแแแขแ แแแแแ แฎแแแแแแก แแ แแแแแ แกแฎแแ แแแแแแแจแแแแแแ แแ แแแ. แแฅแแแแ แแแแ แแแขแแแแขแฃแ แแ แแกแแ แแก แญแฃแ แแแแก แแ แแฅแแแ แชแแแแแแ แแแแ แขแงแแ แกแฎแแ แแแแแแแจแแแแก, แฎแแแ แแแแแแแ แแแชแแแแ แแแแ แญแฃแ แแแแ.
1. แแ แแแฅแขแแก แแแแแ แแแแแฎแแแแ/แกแขแ แฃแฅแขแฃแ แ
แแฃแ แฉแแ
แฉแแแแขแแแ แแแ แกแแฌแงแแกแ แแแแ แกแแแแแแแแแ แแแแแจแ, แแกแ แ แแ แแฅแแแ แจแแแแซแแแแ แแแแงแแแ.
แแแแแแแแ แแงแแแแแก แจแแแแแแก:
แแฅแกแแ แแกแ แแ แแก แงแแแแแแ แแแแฃแแแ แฃแแ Node.js แแแ แฉแแ แฉแ, แ แแแแแแช แแแ แแแแก แแแแแจแแก แแแ แกแแ แแแ แก.แกแแแแขแ โ แแแแกแแแแขแแก แแแแแแแแแแ แแ แแฃแแแ แกแ แแ แกแแ แแแ แก แจแแ แแก แแแแแชแแแแ แแแชแแแแกแแแแก.แแแแแแแ แแ - แแแแฃแแแก แแแแแฏแแ แ. แแฅแแแ แจแแแแซแแแแ แฌแแแแแแฎแแ แแแแก แจแแกแแฎแแ, แแฃ แ แแขแแ แฃแแแ แแแแแแงแแแแ Webpackแแฅ .
แแกแ แแแแแแงแฃแ แแแ แแ แแแฅแขแแก แแแ แแฅแขแแ แแแก แกแขแ แฃแฅแขแฃแ แ:
public/
assets/
...
src/
client/
css/
...
html/
index.html
index.js
...
server/
server.js
...
shared/
constants.js
แแฃแแแขแแ แแ/
แงแแแแแคแแ แ แกแแฅแแฆแแแแแจแ public/
แกแขแแขแแแฃแ แแ แแฅแแแแ แแแแแแแแแแ แกแแ แแแ แแก แแแแ . IN public/assets/
แจแแแชแแแก แฉแแแแ แแ แแแฅแขแแก แแแแ แแแแแงแแแแแฃแ แกแฃแ แแแแแก.
src /
แงแแแแ แฌแงแแ แแก แแแแ แกแแฅแแฆแแแแแจแแ src/
. แขแแขแฃแแแแ client/
ะธ server/
แแแแแ แแแแแแ แแแแแกแแแแก แแ shared/
แจแแแชแแแก แแฃแแแแ แคแแแแก, แ แแแแแแช แแแแแ แขแแ แแแฃแแแ แ แแแแ แช แแแแแแขแแก, แแกแแแ แกแแ แแแ แแก แแแแ .
2. แแฌแงแแแ/แแ แแแฅแขแแก แแแ แแแแขแ แแแ
แ แแแแ แช แแแแแ แแฆแแแแจแแ, แแ แแแฅแขแแก แแกแแจแแแแแแแ แแแงแแแแแ แแแแฃแแแก แแแแแฏแแ แก.
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
แแ แแก Javascript (JS) แแแแแแขแแก แจแแกแแแแก แฌแแ แขแแแ. Webpack แแแแฌแงแแแ แแฅแแแแ แแ แ แแแฃแ แกแแฃแแแ แแแซแแแแแก แกแฎแแ แแแแแ แขแแ แแแฃแ แคแแแแแแก.- แฉแแแแ Webpack build-แแก แแแแแแแแแแ JS แแแแแแแกแแแแ แแแ แแฅแขแแ แแแจแ
dist/
. แแ แคแแแแก แฉแแแแก แแแแแ แฅแแแ JS แแแแแขแ. - แฉแแแ แแแงแแแแแ
Babel แแ แแแ แซแแ แแแแคแแแฃแ แแชแแ@babel/preset-env แฉแแแแ JS แแแแแก แแแแแกแแขแแแแ แซแแแแ แแ แแฃแแแ แแแแกแแแแก. - แฉแแแ แแแงแแแแแ แแแแแแแขแก, แ แแ แแแแแแฆแแ แงแแแแ CSS, แ แแแแแแช แแแแแแแแฃแแแ JS แคแแแแแแแ แแ แแแแแแ แแแแแแ แแกแแแ แแ แ แแแแแแแก. แฉแแแแก แแแแแ แฅแแแ 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
แฌแแ แแแแแแจแ แแแแแแแกแแแแกแแก แแแแแขแแก แแแแแก แแแขแแแแแแชแแ.
แแแแแแแแ แแแ แแแงแแแแแ
แแ แแแ แฉแแแ แแแแแแกแขแแแแ แแ แแ แแแฅแขแ แแแแแแแแ แแ แแแแฅแแแแแ, แแกแ แ แแ แแฅแแแ แจแแแแซแแแแ แแแงแแแ แแ แแแกแขแจแ แฉแแแแแแแแ แแแแแฏแแแก. แแแงแแแแแ แแแ แขแแแแ: แแแ แแแ แ แแแจแ, แกแแกแขแแแ แฃแแแ แแงแแก แแแแแกแขแแแแ แแแฃแแ
$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm install
แแ แแแแ แฎแแ แฌแแกแแกแแแแแแ! แแแแแแแแ แแแแก แกแแ แแแ แแก แแแกแแฌแงแแแแ, แฃแแ แแแแ แแแฃแจแแแ
$ npm run develop
แแ แแแแแแแ แแฅแแแแก แแ แแฃแแแ แจแ
3. แแแแแแขแแก แจแแกแแแแก แแฃแแฅแขแแแ
แแแแแ แแแแแแแแแ แแแแแ แแแแแจแแก แแแแแ. แฏแแ แแแญแแ แแแแ แแแแ แแ index.html
, แ แแแแกแแช แแฅแแแ แแฌแแแแ แกแแแขแก, แแ แแฃแแแ แ แแแ แแแ แ แแแจแ แฉแแขแแแ แแแแก แแแก. แฉแแแแ แแแแ แแ แกแแแแแแ แแแ แขแแแ แแฅแแแแ:
index.html
แแแแแแแแ .io แแแแแจแ แแแแแแจแ
แแก แแแแแก แแแแแแแแ แแแแแ แแแแแ แขแแแแ แกแแชแฎแแแแกแแแแก แแ แแแแแแก แแแแแแแแแ แแแกแขแแก แแแแ แกแฎแแ แแแแแแแแแแ แแ แแแ. แแฅแแแ แงแแแแแแแแก แจแแแแซแแแแ แแแฎแแ แกแ แฃแแ แแแแ แแฅ
แฒฉแแแ แแแแฅแแก:
HTML5 แขแแแแก แแแแแแแขแ (<canvas>
), แ แแแแแกแแช แแแแแแแงแแแแแ แแแแแจแแก แแแกแแคแแ แแแแแแ.<link>
แฉแแแแ CSS แแแแแขแแก แแแกแแแแขแแแแแ.<script>
แฉแแแแ Javascript แแแแแขแแก แแแกแแแแขแแแแแ.- แแแแแแ แ แแแแแฃ แแแแฎแแแ แแแแแก แกแแฎแแแแ
<input>
แแ แฆแแแแแ "PLAY" (<button>
).
แกแแฌแงแแกแ แแแแ แแแก แฉแแขแแแ แแแแก แจแแแแแ, แแ แแฃแแแ แ แแแแฌแงแแแก Javascript แแแแแก แจแแกแ แฃแแแแแก, แแแฌแงแแแฃแแ แจแแกแแแแก แฌแแ แขแแแแก 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);
};
});
แแก แจแแแซแแแแ แ แแฃแแแ แแฆแแ แแแก, แแแแ แแ แแฅ แแแแ แ แ แแ แแ แฎแแแแ:
- แ แแแแแแแแ แกแฎแแ JS แคแแแแแก แแแแแ แขแ.
- CSS-แแก แแแแแ แขแ (แแกแ แ แแ, Webpack-แแ แแชแแก แแแแ แฉแแ แแแ แฉแแแแก CSS แแแแแขแจแ).
- ะะฐะฟััะบ
connect()
แกแแ แแแ แแแ แแแแจแแ แแก แแแแงแแ แแแ แแ แแแจแแแแdownloadAssets()
แแแแแจแแก แแแกแแคแแ แแแแแแ แกแแญแแ แ แกแฃแ แแแแแแก แแแแแแกแแฌแแ แแ. - แแ-3 แแขแแแแก แแแกแ แฃแแแแแก แจแแแแแ แแแแแฉแแแแแ แแแแแแ แ แแแแแฃ (
playMenu
). - "PLAY" แฆแแแแแแก แแแฌแแแแฃแแแแแก แแแแแฃแจแแแแแแแก แแแงแแแแแ. แฆแแแแแแ แแแญแแ แแกแแก, แแแแ แแฎแแแแก แแแแแจแแก แแแแชแแแแแแแแแก แแ แแฃแแแแแ แกแแ แแแ แก, แ แแ แฉแแแ แแแแ แแแ แ แแแแแแแจแแ.
แฉแแแแ แแแแแแข-แกแแ แแแ แแก แแแแแแแก แแแแแแ แ "แฎแแ แชแ" แแ แแก แแ แคแแแแแแจแ, แ แแแแแแแช แแแแแ แขแแ แแแฃแแแ แคแแแแแก แแแแ index.js
. แแฎแแ แฉแแแ แแแแแแฎแแแแแ แแแ แงแแแแ แแแแแแแแแแ แแแแ.
4. แแแแแแขแแก แแแแแชแแแแแแก แแแชแแแ
แแ แแแแแจแจแ แฉแแแ แแแงแแแแแ แชแแแแแ แแแแแแแแแแแก แกแแ แแแ แแแ แแแแฃแแแแแชแแแกแแแแก
แแแแฅแแแแ แแ แแ แคแแแแ src/client/networking.js
แแแแช แแแ แฃแแแแก แงแแแแแก แแแแ แแแแฃแแแแแชแแ แกแแ แแแ แแแ:
แฅแกแแแจแ.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. แแแแแแขแแก แ แแแแแ แ
แแ แแ แแแแแแแฉแแแแ แกแฃแ แแแ แแแ แแแแ!
...แแแแ แแ แกแแแแ แแแแก แแแแแแแแแแ, แฉแแแ แฃแแแ แแแแแแแฌแแ แแ แงแแแแ แกแฃแ แแแ (แ แแกแฃแ แกแ), แ แแแแแแช แกแแญแแ แแ แแแแกแแแแก. แแแแแ แแแแฌแแ แแ แ แแกแฃแ แกแแแแก แแแแแฏแแ แ:
แแฅแขแแแแแ.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
.
แ แแกแฃแ แกแแแแก แฉแแแแขแแแ แแแแก แจแแแแแ แจแแแแซแแแแ แแแแฌแงแแ แ แแแแแ แ. แ แแแแ แช แฃแแแ แแฆแแแแจแแแ, แฉแแแ แแแงแแแแแ แแแ แแแแ แแแ แแแกแแฎแแขแแ <canvas>
). แฉแแแแ แแแแแจแ แกแแแแแแ แแแ แขแแแแ, แแแแขแแ แฉแแแ แแฎแแแแ แจแแแแแแ แฃแแแ แแแแฎแแขแแ:
- แคแแแแก
- แแแแแแแจแ แแแแ
- แกแฎแแ แแแแแแแจแแแแ แแแแแจแจแ
- แญแฃแ แแแแ
แแฅ แแ แแก แแแแจแแแแแแแแแ แคแ แแแแแแขแแแ 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
:
แจแแงแแแแ.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()
(of networking.js
) แ แแแแกแแช แฎแแแแ แจแแงแแแแแก แแแแแแแ (แแแแแแแแแ, แแแฃแกแแก แแแแแแแแแแแแแกแแก). updateDirection()
แแฎแแแ แจแแขแงแแแแแแแแแแก แแแชแแแแก แกแแ แแแ แแแ, แ แแแแแแช แแแฃแจแแแแแก แจแแงแแแแแก แแแแแแแแก แแ แจแแกแแแแแแกแแ แแแแแแฎแแแแก แแแแแจแแก แแแแแแแ แแแแแก.
7. แแแแแแขแแก แกแขแแขแฃแกแ
แแก แแแแงแแคแแแแแ แงแแแแแแ แ แแฃแแแ แแแกแขแแก แแแ แแแ แแแฌแแแจแ. แแฃ แแแแแแแชแ แฃแแแ, แแฃ แแ แแแกแแแ แแแ แแแแแ แฌแแแแแฎแแแกแแก! แแฅแแแ แแ แจแแแแซแแแแ แแแแแขแแแแ แแแ แแ แแแแแแแแแแแ แแแฃแแ แฃแแแแ แแแก.
แแแแแแข-แกแแ แแแ แแก แแแแแก แแแกแแกแ แฃแแแแแแ แกแแญแแ แ แแแแกแแขแแฎแแก แแแแ แแแฌแแแแ แแงแ. แแแฎแกแแแ แแแแแก แแแฌแงแแแขแ Client Rendering แแแแงแแคแแแแแแแแ?
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: แแแคแแ แแแชแแ แแแแแแแจแแก แจแแกแแฎแแ, แ แแแแแแช แแฆแแแก แแ แแแแแฎแแแแแก.
- แกแฎแแแแ: แแแคแแ แแแชแแแก แแแกแแแ แกแฎแแ แแแแแแแจแแแแแก แจแแกแแฎแแ, แ แแแแแแแช แแแแแฌแแแแแแแ แแแแแ แแแแแจแจแ.
- แขแงแแแแแ: แแแคแแ แแแชแแแก แแแกแแแ แแแแแจแจแ แญแฃแ แแแแแก แจแแกแแฎแแ.
- leaderboard: แแแแแ แแแ แแแก แแแแแแแแ แ แแแแแชแแแแแ. แฉแแแ แแแ แแ แแแแแแแแแแกแฌแแแแแ แแ แแแกแขแจแ.
7.1 แแฃแแฃแแ แงแแแแ แแแแแแขแแก แแแแแแแ แแแแ
แแฃแแฃแแ แงแแแแ แแแแฎแแ แชแแแแแแ getCurrentState()
แจแแฃแซแแแ แแแ แแแแแ แแแแแ แฃแแแก แแแแแชแแแแแ แแแแแจแแก แแแแ แแแแแฎแแแแแแแ.
แแฃแแฃแแ แงแแแแ-แกแแฎแแแแฌแแคแ.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.
Tick โโRate: แกแแฎแจแแ แ, แ แแแแแแแช แกแแ แแแ แ แฃแแแแแแแก แแแแแจแแก แแแแแฎแแแแแแก แแแแแแขแแแก. แแก แฎแจแแ แแ แฃแคแ แ แแแแแแแ แแแแ แ แแแแ แแแแก แกแแฎแจแแ แ. แฉแแแแก แแแแแจแจแ แกแแ แแแ แ แแฃแจแแแแก แฌแแแจแ 30 แขแแแแแ.
แแฃ แฉแแแ แฃแแ แแแแ แแแแแแแขแแแ แแแแแจแแก แฃแแฎแแแก แแแแแฎแแแแแก, แแแจแแ FPS แแ แกแแแแแแ แแ แแกแแแแก แแแแแแญแแ แแแแก 30-แก, แ แแแแแ แฉแแแ แแ แแกแแ แแก แแแฆแแแ 30-แแ แแแข แแแแแฎแแแแแก แกแแ แแแ แแแแ แฌแแแจแ. แแฃแแแแช แแแแฃแ แแแแ render()
60-แฏแแ แฌแแแจแ, แแแจแแ แแ แแแ แแแแก แแแฎแแแแ แ แฃแแ แแแแ แแแแแฎแแแแแก แแแแแแก, แแ แกแแแแแแ แแ แแคแแ แก แแแแแแแก. แแฃแแฃแแ แงแแแแ แแแแฎแแ แชแแแแแแแก แแแแแ แแ แแ แแ แแแแแแ แแ แแก แแก แแฅแแแแแแแแ แแแ แแแแแแแแแแแก. แแแแแแฃแ แ แแแขแแ แแแขแแก แกแแฉแฅแแ แแ, แแแแแแขแ แแแแฆแแแก แแแแแจแแก แแแแแฎแแแแแก แแฃแกแขแแ แงแแแแ 33 ms (30 แฌแแแจแ):
แกแแแฌแฃแฎแแ แแ, แแแแแแฃแ แ แแ แแคแแ แแ. แฃแคแ แ แ แแแแฃแ แ แกแฃแ แแแ แแฅแแแแ:
แแฃแแฃแแ แงแแแแ แแแแฎแแ แชแแแแแแ แงแแแแแแ แฃแแ แแกแ แจแแแแฎแแแแแ, แ แแแแกแแช แกแแฅแแ แแแขแแแขแฃแ แแแแก แแฎแแแ. แแฃ แแแแแจแแก แแแแแฎแแแแ แแแแฆแแแ 50 ms แแแแแแแแแแแ, แแแจแแ แแแแแแขแแ แกแแแแแแแแ แแแแแขแแแแ 50 ms-แแ, แ แแแแแ แแก แแแแแ แแฎแแแแก แแแแแจแแก แแแแแแแ แแแแแก แฌแแแ แแแแแฎแแแแแแแ. แแฅแแแ แฌแแ แแแแแแแแแ, แ แแแแแแแ แแแฃแฎแแ แฎแแแแแแ แแก แแแแแแแจแแกแแแแก: แแแแแแแแฃแ แ แจแแแแแแแแก แแแแ, แแแแแจแ แแแฉแแแแแแแแ แแ แแ แแกแขแแแแแฃแ แ แแฅแแแแ.
7.2 แแแแแแขแแก แแแแแแแ แแแแแก แแแฃแแฏแแแแกแแแ
แฉแแแ แแแแแแฎแแ แชแแแแแแ แแแ แแแแฃแ แแแฃแแฏแแแแกแแแแก แแฃแแฃแแ แงแแแแ แแแแฎแแ แชแแแแแแแจแ. แแแ แแแ แ แแแจแ, แฉแแแ แแแงแแแแแ แแแแแชแแแแก แจแแคแแ แฎแแแ 100 ms-แแกแแแแก. แแก แแแจแแแแก, แ แแ แแแแแแขแแก "แแแแแแแแ แ" แแแแแแแ แแแแ แงแแแแแแแแก แฉแแแแ แฉแแแ แกแแ แแแ แแ แแแแแจแแก แแแแแแแ แแแแแก 100 ms-แแ. แแแแแแแแแ, แแฃ แกแแ แแแ แแ แแ แ แแ แแก 150, แจแแแแแ แแแแแแขแ แแแแแแขแแแก แแ แแแแแแแ แแแแแก, แ แแแแแจแแช แกแแ แแแ แ แแ แแ แแก แแแงแแคแแแแแ 50:
แแก แแแแซแแแแก 100 ms แแฃแคแแ แก, แ แแ แแแแแแ แฉแแ แแแแแจแแก แแแแแฎแแแแแแแก แแ แแแ แแแแแแแ แแแแแ แแ แแแ:
แแแแก แแแแแฆแแฃแ แแแ แแฃแแแแแ แแฅแแแแ
แฉแแแ แแกแแแ แจแแแแแซแแแ แแแแแแแงแแแแ แกแฎแแ แขแแฅแแแแ แ.แฌ
แแแแแแขแแก แแฎแ แแแแ แแ แแแแแแ , แ แแแแแแช แแแ แ แกแแฅแแแก แแแแแแแก แแฆแฅแแฃแแ แจแแงแแแแแแแก แจแแกแแแชแแ แแแแแ, แแแแ แแ แแ แแแกแขแจแ แแ แแฅแแแแ แแแจแฃแฅแแแฃแแ.
แแแแแ แแ แแ แแแฃแแฏแแแแกแแแ, แ แแแแแกแแช แฉแแแ แแแงแแแแแ แแ แแก แฌแ แคแแแ แแแขแแ แแแแแชแแ. แ แแแแแ แแแแแก แจแแคแแ แฎแแแแก แแแแ, แฉแแแ แฉแแแฃแแแแ แแ แแแแแแแแ แแแแแแฃแ แแ แแ แแแแแฎแแแแแก แแแแแแขแจแ แแแแแแแแ แ แแ แแแ แแแ แ. แ แแชแ แแแฃแซแแฎแแก getCurrentState()
, แฉแแแ แจแแแแแซแแแ แจแแแแกแ แฃแแแ
แแก แฌแงแแแขแก แแแแ แแแแก แกแแฉแฅแแ แแก แแ แแแแแแแก: แแฎแแ แฉแแแ แจแแแแแซแแแ แแแแแแฎแแขแแ แฃแแแแแแฃแ แ แแแแ แแแ แฉแแแแแแแก แกแแญแแ แ แแแแแกแแแแ แ แกแแฉแฅแแ แแ!
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()
. แ แแแแ แช แแแ แ แแแแฎแแ, แแแแแจแแก แงแแแแ แแแแแฎแแแแ แจแแแชแแแก แกแแ แแแ แแก แแ แแแก แแแจแแแก. แฉแแแ แแแแแแ แแแแแแแงแแแแ แ แแแแแ แแก แจแแงแแแแแแ, แ แแแ แแแแแแแขแแแแ แกแฃแ แแแ 100แแ-แแ แกแแ แแแ แแก แฃแแแ, แแแแ แแ แฉแแแ แแแ แแกแแแแก แแแแแแแแ แกแแ แแแ แแ แแแแแแแแ แ แแ แแก, แ แแแแแ แฉแแแ แแ แแแชแแ แ แแแแแแ แแ แ แแแกแญแแ แแ แ แแแแแแแ แแแแแฎแแแแแก แฉแแแแแแแ แแแกแแแแก. แแแขแแ แแแขแ แแ แแแ แแแแแแแ แแแแแแ แแ แแแกแ แกแแฉแฅแแ แ แจแแแซแแแแ แแแแจแแแแแแแแแ แแแแกแฎแแแแแแแแแแก!
แแ แแ แแแแแแแก แแแแแกแแญแ แแแแ, แฉแแแ แจแแแแแซแแแ แแแแแแแงแแแแ แแแแแแ แฃแแ แแแแฎแแแแแ: แฉแแแ แแแแแ แแแคแแฅแ แแ, แ แแ แแแ แแแแ แแแแแฎแแแแ แแงแแกแแแ แแ แแแแแแ. แแฃ แแก แกแแแแ แแแ แแงแ, แแแจแแ แฉแแแ แแแแชแแแแแแแ แกแแ แแแ แแก แแ แ แแ แแแแแ แแขแฃแ แแแแแแขแจแ! แฉแแแ แแแแแฎแแแ แกแแ แแแ แแก แแ แแแก แแแจแแแก firstServerTimestamp
แแ แแแแแแแ แฉแแแแ แฉแแแแ แแแแแแแแ แแแ (แแแแแแขแ) แแ แแแก แแแแแแญแแ แแแแแ แแแแแแขแจแ gameStart
.
แฒแแแชแแแ. แแก แแ แฃแแแ แแงแแก แกแแ แแแ แแก แแ แ = แแแแแแขแแก แแ แ? แ แแขแแ แแแแแแกแฎแแแแแแ โแกแแ แแแ แแก แแ แแแก แแแจแแแกโ แแ โแแแแแแขแแก แแ แแแก แแแจแแแกโ แจแแ แแก? แแก แแแแ แแแแฎแแแ! แแแแแแแก, แ แแ แแก แแ แแ แแก แแ แแ แแ แแแแแ. Date.now()
แแแแแ แฃแแแแก แกแฎแแแแแกแฎแแ แแ แแแก แแแจแแแก แแแแแแขแกแ แแ แกแแ แแแ แแ แแ แแก แแแแแแแแแแฃแแแ แแ แแแแฅแแแแแแก แแแแแแแแ แแ แคแแฅแขแแ แแแแ. แแ แแกแแแแก แแคแแฅแ แแ, แ แแ แแ แแแก แแแแแแญแแแแ แงแแแแ แแแแฅแแแแแ แแ แแแแแ แ แแฅแแแแ.
แแฎแแ แฉแแแ แแแแกแแแก, แ แแก แแแแแแแก currentServerTime()
: แแ แฃแแแแแ แกแแ แแแ แแก แแ แแแก แแแแแแญแแ แแแแแแแแ แ แ แแแแแ แแก แแ แแแก. แกแฎแแ แกแแขแงแแแแแ แ แแ แแแฅแแแ, แแก แแ แแก แกแแ แแแ แแก แแแแแแแแ แ แแ แ (firstServerTimestamp <+ (Date.now() - gameStart)
) แแแแฃแก แแแแแชแแแแก แแแแแแแแแแ (RENDER_DELAY
).
แแฎแแ แแแแแ แจแแแฎแแแแ, แแฃ แ แแแแ แแแฌแแ แแแแแ แแแแแจแแก แแแแแฎแแแแแแก. แ แแแแกแแช แแแแแฎแแแแ แแแแฆแแแ แกแแ แแแ แแแแ, แแแก แแซแแฎแแแ processGameUpdate()
แแ แฉแแแ แแแแแฎแแแ แแฎแแ แแแแแฎแแแแแก แแแกแแแจแ gameUpdates
. แจแแแแแ, แแแฎแกแแแ แแแแก แแแแแงแแแแแแก แจแแกแแแแฌแแแแแแ, แฉแแแ แแจแแแ แงแแแแ แซแแแ แแแแแฎแแแแแก แแแแแก แแแแแฎแแแแแ แแแแแ แแกแแแ แแฆแแ แแแญแแ แแแแ.
แ แ แแ แแก โแซแแ แแแแแ แแแแแฎแแแแโ? แแก แแแ แแแแ แแแแแฎแแแแ, แ แแแแแกแแช แแแแฃแแแแ แกแแ แแแ แแก แแแแแแแแ แ แแ แแแแแ แฃแแแ แแแแแแแแแแแแแ. แแแฎแกแแแ แแก แแแแแ แแแ?
แแแแแจแแก แแแแแฎแแแแ แแแ แแแแแ "Client Render Time"-แแก แแแ แชแฎแแแ แแ แแก แกแแแแแแกแ แแแแแฎแแแแ.
แ แแกแแแแก แแแแแแงแแแแแ แแแแแก แแแแแฎแแแแ? แ แแขแแ แจแแแแแซแแแ แฉแแแแแแแแแ แแแแแฎแแแแแแ แแแแแแ? แแแแก แแแกแแแแแแ, แแแแแ แแแแแก แแ แแแแแก แแแแแฎแแแแ แแแแฎแแ แชแแแแแแ 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),
};
}
}
แฉแแแ แแแแแแฎแแแแแ แกแแ แจแแแแฎแแแแแก:
base < 0
แแแจแแแแก, แ แแ แแ แแ แแก แแแแแฎแแแแแแ แแแแแแแแ แ แ แแแแแ แแก แแ แแแแ (แแฎ. แแแแแ แแแแฎแแ แชแแแแแแแกgetBaseUpdate()
). แแก แจแแแซแแแแ แแแฎแแแก แแแแแจแแก แแแฌแงแแแแกแแแแแแ, แ แแแแแ แแก แฉแแแแ แฉแแแแก แแแแ. แแ แจแแแแฎแแแแแจแ, แฉแแแ แแแงแแแแแ แแแฆแแแฃแ แฃแแฎแแแก แแแแแฎแแแแแก.base
แแ แแก แฃแแฎแแแกแ แแแแแฎแแแแ, แ แแช แแแแฅแแก. แแก แจแแแซแแแแ แแแฎแแแก แฅแกแแแแก แจแแงแแแแแแแก แแ แชแฃแแ แแแขแแ แแแขแแก แแแแ. แแ แจแแแแฎแแแแแจแแช แฉแแแ แแแงแแแแแ แฃแแฎแแแก แแแแแฎแแแแแก, แ แแช แแแแฅแแก.- แฉแแแ แแแแฅแแก แแแแแฎแแแแ แ แแแแ แช แแแแแแแแ แ แ แแแแแ แแก แแ แแแแ, แแกแแแ แแแก แจแแแแแ, แแกแ แ แแ, แจแแแแแซแแแ แแแขแแ แแแแแชแแ!
แงแแแแแคแแ แ แ แแช แจแแแแ แฉแ state.js
แแ แแก แฎแแแแแแแ แแแขแแ แแแแแชแแแก แแแแฎแแ แชแแแแแแ, แ แแแแแแช แแแ แขแแแ (แแแแ แแ แแแกแแฌแงแแแ) แแแแแแแขแแแแ. แแฃ แแกแฃแ แ แแแแแ แจแแแกแฌแแแแแ, แแแจแแ แแแฎแกแแแแ state.js
on
แแแฌแแแ 2. Backend แกแแ แแแ แ
แแ แแแฌแแแจแ แฉแแแ แแแแแแฎแแแแแ Node.js backend-แก, แ แแแแแแช แแแแแขแ แแแแแก แฉแแแแก
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 แแแแคแแแฃแ แแชแแแแก. แฉแแแ แแแแแแแงแแแแแ แแแ แแ แ แแแแ:
- แแแแแแงแแแแ
webpack-dev-middleware แ แแ แแแขแแแแขแฃแ แแ แแฆแแแแแแแแ แฉแแแแ แแแแแแแแ แแแแก แแแแแขแแแ, แแ - แกแขแแขแแแฃแ แแ แแแแแแขแแแแ แกแแฅแแฆแแแแ
dist/
, แ แแแแแจแแช Webpack แฉแแฌแแ แก แฉแแแแก แคแแแแแแก แฌแแ แแแแแแก แจแแฅแแแแก แจแแแแแ.
แแแแแ แแ แแ แแแแจแแแแแแแแแ แแแแชแแแ server.js
แจแแแแแแ แกแแ แแแ แแก แแแงแแแแแแกแแแ
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 แแแแจแแ แแก แฌแแ แแแขแแแแ แแแแงแแ แแแแก แจแแแแแ, แฉแแแ แแแแแแคแแแฃแ แแ แแแ แแแแแแแแก แแแแแฃแจแแแแแแแแก แแฎแแแ แกแแแแขแแกแแแแก. แแแแแแแแก แแแแแฃแจแแแแแแแแ แแแฃแจแแแแแแ แแแแแแขแแแแกแแแ แแแฆแแแฃแ แจแแขแงแแแแแแแแแก singleton แแแแแฅแขแแ แแแแแแแ แแแแ 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
แแกแ แ แแ, แฉแแแ แแ แแแญแแ แแแแ แแแแแ แคแแฅแ แ. แแ แแแก แแแแฃแ แแแแ แแแแแแแจแแก ID.
แแแแก แแแแแแแแกแฌแแแแแแ, แแแแแ แแแแแแแแแแแแ แแแกแขแแแชแแแก แชแแแแแแแ แแแแกแจแ Game
:
sockets
แแ แแก แแแแแฅแขแ, แ แแแแแแช แแแแแจแแ แแแก แแแแแแแจแแก ID-แก แกแแแแขแแแ, แ แแแแแแช แแแแแแจแแ แแแฃแแแ แแแแแแแจแแกแแแ. แแก แกแแจแฃแแแแแแก แแแแซแแแแก, แแ แแแ แแแแแแแแแแแจแ, แฌแแแแแ แแแแแฆแแ แกแแแแขแแแแ แแแแ แแแแแแแจแแก ID-แแแแ.players
แแ แแก แแแแแฅแขแ, แ แแแแแแช แแแแแจแแ แแแก แแแแแแแจแแก ID-แก แแแแแแ>แแแแแแแจแแก แแแแแฅแขแแแ
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()
แจแแแชแแแก แกแแ แแแ แแก แแฎแแ แแก แแแแแแแก แแแแแ แงแแแแแแ แแแแจแแแแแแแแ แแแฌแแแก. แแแแแ แฉแแแแแแแแแแ แงแแแแแคแแ แ, แ แแกแแช แแแแแแแก แแแแแแแแแแ แแแแ:
- แแแแแแก แ แแแแแ แกแแแแแ
dt
แแก แแ แแก แแแแแแแupdate()
. - แแแแแแฎแแแแก แแแแแแฃแ แญแฃแ แแก แแ แกแแญแแ แแแแแก แจแแแแฎแแแแแจแ แแแแแแฃแ แแแก แแแ. แแ แคแฃแแฅแชแแแก แแแแฎแแ แชแแแแแแแก แแแแแแแแแแแ แแแฎแแแแแ. แฏแแ แฏแแ แแแแ แกแแแแแ แแกแแ แแแแก แชแแแแ
bullet.update()
แแ แฃแแแแแtrue
แแฃ แญแฃแ แแ แฃแแแ แแแแแแแฃแ แแแก (แแก แแ แแแแแแ แแแแแแ). - แแแแแแฎแแแแก แแแแแแฃแ แแแแแแแจแแก แแ แกแแญแแ แแแแแก แจแแแแฎแแแแแจแ แฅแแแแก แญแฃแ แแแแก. แฉแแแ แแกแแแ แแแฎแแแแแ แแ แแแแฎแแ แชแแแแแแแก แแแแแแแแแแแ -
player.update()
แจแแฃแซแแแ แแแแแฅแขแแก แแแแ แฃแแแแBullet
. - แแแแฌแแแแก แจแแฏแแฎแแแแก แญแฃแ แแแแกแ แแ แแแแแแแจแแแแก แจแแ แแก
applyCollisions()
, แ แแแแแแช แแแ แฃแแแแก แญแฃแ แแแแแก แแแกแแแก, แ แแแแแแช แแแฎแแแ แแแแแแแจแแแแก. แแแแแแฃแแ แแแแ แฃแแแแฃแแ แญแฃแ แแแกแแแแก, แฉแแแ แแแ แแแ แแ แแแแแแแจแแก แฅแฃแแแก, แ แแแแแแแช แแก แแกแ แแแ (แแแแแงแแแแแแplayer.onDealtDamage()
) แแ แจแแแแแ แแแแแฆแแ แญแฃแ แแ แแแกแแแแแแbullets
. - แแชแแแแแแก แแ แแแแแแฃแ แแแก แงแแแแ แแแแแฃแ แแแแแแแจแแก.
- แฃแแแแแแแก แแแแแจแแก แแแแแฎแแแแแก แงแแแแ แแแแแแแจแแก แงแแแแ แฌแแแก แฏแแ แ แแชแ แแซแแฎแแแ
update()
. แแก แแแแฎแแแ แแแ แแแแแงแฃแ แ แแแแแแแ แแแแแ แแแฎแกแแแแ แแแแฎแแแ แ แชแแแแแก.shouldSendUpdate
... แ แแแแ แชupdate()
แแแ แแแแ 60-แฏแแ /แฌแ, แฉแแแ แแแแแแแแแ แแแแแจแแก แแแแแฎแแแแแแก 30-แฏแแ /แฌแ. แแแ แแแแ, แกแแแแแก แกแแฎแจแแ แ แกแแ แแแ แแก แกแแแแ แแ แแก 30 แกแแแแ/แฌแ (แฉแแแ แแแ แแแ แแแฌแแแจแ แแแกแแฃแแ แแ แกแแแแแก แแแแแแแแแแแแ).
แ แแขแแ แแแแแแแแแ แแฎแแแแ แแแแแจแแก แแแแแฎแแแแแแ แแ แแแ แแแแแแแแแแแจแ ? แแ แฎแแก แจแแกแแแแฎแแ. แแแแแจแแก 30 แแแแแฎแแแแ แฌแแแจแ แแแแ แแ!
แ แแขแแ แแ แแแ แแแ แแแจแแ?
update()
30 แฏแแ แฌแแแจแ? แแแแแจแแก แกแแแฃแแแชแแแก แแแกแแฃแแฏแแแแกแแแแแ. แ แแช แฃแคแ แ แฎแจแแ แแ แแซแแฎแแแ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
:
แแแแแฅแขแ.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
:
แขแงแแแ.js
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
:
แแแแแแแจแ.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
:
แแแแแจแ.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
.
แแ, แ แแแแ แแแแแแงแฃแ แแแ แฉแแแแ แจแแฏแแฎแแแแก แแแแแแแแแแก แแแแฎแแ แชแแแแแแ:
แจแแฏแแฎแแแแแ.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;
}
แจแแฏแแฎแแแแก แแก แแแ แขแแแ แแฆแแแฉแแแ แแคแฃแซแแแแ แแ แคแแฅแขแก, แ แแ แแ แ แฌแ แ แแฏแแฎแแแ, แแฃ แแแ แชแแแขแ แแแก แจแแ แแก แแแแซแแแ แแแแแแแแ แแแแ แ แแแแฃแกแแแแก แฏแแแแ. แแฅ แแ แแก แจแแแแฎแแแแ, แ แแแแกแแช แแแแซแแแ แแ แ แฌแ แแก แชแแแขแ แแแก แจแแ แแก แแฃแกแขแแ แฃแแ แแก แแแแ แ แแแแฃแกแแแแก แฏแแแก:
แแฅ แงแฃแ แแแฆแแแ แฃแแแ แแแแฅแชแแแ แแแแแ แ แแแแแแแแ แแกแแแฅแขแก:
- แญแฃแ แแ แแ แฃแแแ แแแฎแแแแก แแแแแแแจแแก, แ แแแแแแแช แจแแฅแแแ แแแ. แแแแก แแแฆแฌแแแ แจแแกแแซแแแแแแแ แจแแแแ แแแแ
bullet.parentID
ัplayer.id
. - แญแฃแ แแ แฃแแแ แแแฎแแแแก แแฎแแแแ แแ แแฎแแ แแ แจแแแแฆแฃแแแแแ แจแแแแฎแแแแแกแแแแก, แ แแแแกแแช แ แแแแแแแแ แแแแแแแจแ แแ แแแ แแฃแแแ แจแแแฏแแฎแแแ. แฉแแแ แแ แแ แแแแแแแก แแแแแแแแ แแแ แแแแ แแขแแ แแก แแแแแงแแแแแแ
break
: แ แแแแ แช แแ แแฆแแแฉแแแแแ แญแฃแ แญแแแแแ แจแแฏแแฎแแแฃแแ แแแแแแแจแ, แแฌแงแแแขแ แซแแแแแก แแ แแแแแแแแแแ แ แจแแแแแ แญแฃแ แแแ.
แแแแแก
แฒกแฃแ แแก แแ แแก! แฉแแแ แแแแแแฎแแแแ แงแแแแแคแแ แ, แ แแช แแฅแแแ แฃแแแ แแชแแแแ .io แแแ แแแแแจแแก แจแแกแแฅแแแแแแ. แฒ แ แแ แแก แจแแแแแแ? แจแแฅแแแแแ แแฅแแแแ แกแแแฃแแแ แ .io แแแแแจแ!
แงแแแแ แแแแฃแจแ แแแแ แแ แแก แฆแแ แฌแงแแ แ แแ แแแแแแแกแแแฃแแแ
แฌแงแแ แ: www.habr.com