Files
vending-machine-game/README.md
T
HamedandClaude Opus 5.5 6ce560089f
CI/CD / test (push) Successful in 28s
CI/CD / publish (push) Successful in 17s
CI/CD / deploy (push) Successful in 4s
Add camera AR view, GPS stable mode and wider machine range
- Real-world mode opens the phone's rear camera as the game background;
  the 3D camera stands at the GPS position and follows the phone's
  orientation, so machines, beacons, range circles, the route and the
  robot appear in their real direction. City, traffic and family are
  hidden in this view; a toggle switches back to the 3D map, and drag
  turns the view where there is no orientation sensor
- Stable mode (default on): drops GPS jumps impossible on foot, holds the
  position until it drifts past half the GPS error, calms the compass
- Arrival range is 2.5x the GPS error (20–50 m), shown as a ground circle
- Tests for the stabilizer, AR orientation and field of view

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:14:04 +03:30

18 KiB
Raw Blame History

عملیات مشهد — روبات مارکت

بازی سه‌بعدی مرورگری (Three.js) با حال‌وهوای بازی‌های اکشن سوم‌شخص، بدون سلاح و خشونت. یک پدر و فرزندش از کارخانهٔ سازندهٔ دستگاه‌ها راه می‌افتند، در خیابان‌های واقعی مشهد می‌گردند، دستگاه‌های وندینگ روبات مارکت را پیدا می‌کنند و با هر خرید امتیاز می‌گیرند. روبات راهنمای روبات مارکت داخل دستگاه‌ها زندگی می‌کند: وقتی خانواده به یک دستگاه می‌رسند، از آن بیرون می‌آید، خوش‌آمد می‌گوید و راهنمایی‌شان می‌کند.

بازی یک PWA است: روی گوشی نصب می‌شود و آفلاین هم اجرا می‌شود.

🎮 اجرای آنلاین بازی

داستان و هدف

  • شروع: سالن تولید کارخانهٔ شرکت روبات مارکت (بزرگراه مشهد–چناران، خیابان کاله، کنار پارک علم و فناوری خراسان). کارخانه سالن تولید با خط مونتاژ، دفتر، حیاط، دروازه و تابلوی شرکت دارد. ماشین خانواده در حیاط پارک است.
  • مسیر: از کارخانه تا بوستان ملت حدود ۲۰ کیلومتر رانندگی است، از بزرگراه مشهد–چناران، بزرگراه پیامبر اعظم، بزرگراه آزادی و وکیل‌آباد.
  • نقشهٔ ایران: صفحهٔ توجیه مأموریت نقشهٔ استان‌های ایران را نشان می‌دهد. فعلاً فقط مشهد (خراسان رضوی) باز است و شهرهای دیگر قفل‌اند.
  • محدودهٔ قابل بازی: حدود ۱۵ در ۱۸ کیلومتر از شمال‌غرب تا غرب مشهد، همه از داده‌های واقعی OpenStreetMap:
    • جزئیات کامل اطراف کارخانه و محدودهٔ بوستان ملت و دانشگاه فردوسی
    • همهٔ خیابان‌ها و ساختمان‌ها در فاصلهٔ ۲۵۰ متری مسیر رانندگی
    • شبکهٔ خیابان‌های اصلی در کل منطقه
  • دستگاه‌ها: دستگاه نمونهٔ کارخانه (نصف قیمت)، بوستان ملت، مجموعه آموزشی راحیل (فقط ۷:۳۰ تا ۱۳:۳۰)، دانشگاه فردوسی و دانشکدهٔ ادبیات (۲۰٪ تخفیف دانشجویی)، دانشگاه علوم پزشکی، ایستگاه مترو آزادی، مشهد مال (از ساعت ۱۰). همهٔ دستگاه‌ها و محصولاتشان فرضی‌اند.

کنترل

کار صفحه‌کلید موبایل
حرکت / رانندگی W A S D یا جهت‌ها جوی‌استیک
دویدن Shift دکمهٔ «دویدن»
چرخاندن دوربین ماوس (با کلیک قفل می‌شود) یا کشیدن کشیدن روی صفحه
زوم دوربین چرخ ماوس —
سوار و پیاده شدن از ماشین F دکمهٔ F
خرید از دستگاه E دکمهٔ E
کشیدن کارت C دکمهٔ E
ترمز دستی Space —
نقشهٔ شهر M یا کلیک روی نقشهٔ گوشه لمس نقشه

کنترل‌ها با صفحه‌کلید فارسی هم کار می‌کنند. دوربین فقط سوم‌شخص است.

نقشه و مسیریابی

با کلیک روی نقشهٔ گرد گوشهٔ صفحه، نقشهٔ کامل شهر باز می‌شود. فهرست دستگاه‌ها با آدرس، ساعت کاری، باز یا بسته بودن، محصولات و فاصله نمایش داده می‌شود. «مسیریابی تا اینجا» مسیر را روی شبکهٔ واقعی خیابان‌ها پیدا می‌کند (پیاده روی همهٔ مسیرها، با ماشین فقط روی خیابان‌های سواره‌رو). مسیر به شکل خط طلایی روی زمین، روی نقشهٔ گوشه و روی قطب‌نمای بالای صفحه نمایش داده می‌شود. بدون انتخاب دستی، هدف مأموریت فعلی مقصد است.

امتیاز

رویداد امتیاز
هر خرید ۱۰۰
اولین خرید در هر مکان (کشف) ۲۵۰
خرید نوشیدنی و خوراکی از یک دستگاه ۱۵۰
خرید از هر پنج نوع مکان شهری ۵۰۰
خرید در حالت دنیای واقعی ۲۰۰

هفت مأموریت امتیاز و پاداش نقدی (هتومان) دارند. درجه‌ها از «تازه‌وارد» تا «افسانهٔ مشهد» بالا می‌روند. بودجهٔ اولیه ۵۰۰ هتومان است (۱۰ هتومان = ۱۰۰٬۰۰۰ ریال). وقتی پول کافی برای هیچ خریدی نماند، یا همهٔ مأموریت‌ها انجام شود، عملیات تمام می‌شود. هر ثانیهٔ واقعی ده ثانیه از ساعت بازی است.

نصب روی گوشی (PWA)

  • اندروید و Chrome کامپیوتر: دکمهٔ «نصب بازی روی گوشی» در صفحهٔ شروع، یا گزینهٔ Install در منوی مرورگر
  • آیفون: در Safari، دکمهٔ Share و بعد Add to Home Screen. دکمهٔ نصب در بازی همین راهنما را نشان می‌دهد.

آیکون از آواتار روبات روبات مارکت ساخته شده (public/icons). بعد از اولین اجرا همهٔ فایل‌ها، از جمله نقشه و فونت Vazirmatn (داخل پروژه، بدون Google Fonts)، کش می‌شوند و بازی آفلاین اجرا می‌شود. با انتشار نسخهٔ جدید، پیام «نسخهٔ جدید بازی آماده است» نمایش داده می‌شود. Service Worker را vite.config.js در زمان build می‌سازد.

حالت دنیای واقعی (GPS)

دکمهٔ «📍 بازی در دنیای واقعی» در صفحهٔ شروع یا «📍 دنیای واقعی» در نوار پایین این حالت را روشن می‌کند. اول نکته‌های ایمنی نمایش داده می‌شود و بعد اجازهٔ موقعیت و قطب‌نما گرفته می‌شود.

  • خانواده روی نقشهٔ بازی همان‌جایی است که گوشی هست و با راه رفتن واقعی جابه‌جا می‌شود. جهتشان از قطب‌نمای گوشی یا جهت حرکت گرفته می‌شود.

  • موقعیت‌های GPS بر اساس دقتشان نرم و ترکیب می‌شوند. «رسیدن» به دستگاه یعنی رسیدن به محدودهٔ اطرافش، با شعاع ۲٫۵ برابر خطای GPS (بین ۲۰ تا ۵۰ متر). این محدوده با یک دایره روی زمین نشان داده می‌شود. وقتی رسیدید، روبات از دستگاه بیرون می‌آید و خرید ۲۰۰ امتیاز اضافه دارد.

  • حالت پایدار (دکمهٔ «🎯 پایدار»، پیش‌فرض روشن) جلوی پرش‌های GPS را می‌گیرد:

    • پرش‌هایی که با راه رفتن ممکن نیستند نادیده گرفته می‌شوند.
    • تا وقتی جابه‌جایی کمتر از حدود نصف خطای GPS باشد، مکان شما ثابت می‌ماند.
    • قطب‌نما و چرخش دوربین آرام‌تر دنبال می‌شوند.
  • نمای دوربین (AR): با روشن شدن حالت دنیای واقعی، دوربین پشت گوشی پس‌زمینهٔ بازی می‌شود. دوربین بازی در جای گوشی (GPS) و هم‌جهت با گوشی (قطب‌نما و حسگر جهت) قرار می‌گیرد، پس دستگاه‌ها، ستون نور، دایرهٔ محدوده، مسیر طلایی و روبات در جهت واقعی روی تصویر دوربین دیده می‌شوند. مدل شهر و ماشین‌ها در این نما پنهان‌اند. دکمهٔ «📷 دوربین» بین نمای دوربین و نقشهٔ سه‌بعدی جابه‌جا می‌کند. اگر گوشی حسگر جهت نداشته باشد، با کشیدن انگشت روی صفحه می‌چرخید.

  • در سرعت بیش از حدود ۲۲ کیلومتر بر ساعت خرید غیرفعال است. ماشین مجازی هم در این حالت استفاده نمی‌شود.

  • بیرون از محدودهٔ بازی، فاصله تا نزدیک‌ترین دستگاه نمایش داده می‌شود.

  • موقعیت فقط روی خود گوشی پردازش می‌شود. دوربین و قطب‌نما به HTTPS نیاز دارند.

  • اول یک موقعیت سریع و تقریبی از Wi-Fi یا شبکهٔ موبایل گرفته می‌شود و بعد GPS دقیقش می‌کند. وضعیت زیر نام خیابان با کد خطا نمایش داده می‌شود:

    • کد ۱: اجازهٔ Location داده نشده
    • کد ۲: Location گوشی خاموش است
    • کد ۳: هنوز سیگنال نرسیده (جست‌وجو ادامه دارد)

    روی کامپیوتر معمولاً موقعیتی پیدا نمی‌شود، چون Chrome برای کامپیوتر از سرویس موقعیت گوگل استفاده می‌کند که از ایران در دسترس نیست. با گوشی تست کنید.

نقطه‌های آزمایشی GPS: سه دستگاه آزمایشی فقط در حالت دنیای واقعی کنار کارخانه ظاهر می‌شوند، در مختصات 36.450276, 59.423431، 36.450172, 59.423209 و 36.449976, 59.422866. مختصات، نقطه‌ای است که باید آنجا بایستید. با گوشی تا هر نقطه راه بروید: روبات بیرون می‌آید و دکمهٔ خرید فعال می‌شود. شعاع رسیدن ۲٫۵ برابر خطای GPS است (بین ۲۰ تا ۵۰ متر) و همیشه نزدیک‌ترین دستگاه انتخاب می‌شود.

دوربین، GPS و حسگرها فقط روی HTTPS (یا localhost) کار می‌کنند.

اجرا

npm install
npm run dev
npm run build
node --test tests/*.test.js
  • ?quality=low یا دکمهٔ «گرافیک» سایه‌ها را خاموش می‌کند (پیش‌فرض گوشی‌ها).
  • ?debug وضعیت بازی را در window.game در دسترس می‌گذارد.

انتشار روی سرور با Docker

تصویر Docker دو مرحله دارد: Node تست‌ها را اجرا و بازی را build می‌کند، و بعد Caddy فایل‌ها را سرو می‌کند. Caddy گواهی HTTPS را از Let's Encrypt خودکار می‌گیرد و تمدید می‌کند. HTTPS برای نصب روی گوشی و GPS ضروری است.

پیش‌نیاز:

  • رکورد A دامنه‌های robotmarkets.ir و www.robotmarkets.ir به IP سرور اشاره کند.
  • پورت‌های ۸۰ و ۴۴۳ سرور باز و آزاد باشند.
git clone git@github.com:hamedAbdollahzade/vending-machine-game.git && cd vending-machine-game
docker compose up -d --build
docker compose logs -f
  • دامنهٔ دیگر: SITE_ADDRESS="example.ir, www.example.ir" docker compose up -d --build
  • به‌روزرسانی: git pull && docker compose up -d --build. گواهی‌ها در volume caddy_data می‌مانند. بازی‌های نصب‌شده روی گوشی پیام نسخهٔ جدید را نشان می‌دهند.
  • اگر روی سرور از قبل Nginx یا پروکسی دیگری روی ۸۰ و ۴۴۳ هست: در docker-compose.yml پورت‌ها را به "8080:80" تغییر دهید و SITE_ADDRESS=":80" بگذارید. بعد دامنه را در همان پروکسی به 127.0.0.1:8080 بفرستید. در این حالت HTTPS با همان پروکسی است.

سرور داخل ایران: Docker Hub و گاهی npm به IPهای ایران سرویس نمی‌دهند. دو راه وجود دارد:

  1. mirror داخلی برای Docker Hub: در /etc/docker/daemon.json یک mirror تعریف کنید، مثلاً {"registry-mirrors": ["https://docker.arvancloud.ir"]}، و بعد sudo systemctl restart docker.
  2. ساخت روی کامپیوتر خودتان و انتقال تصویر آماده:
    docker compose build
    docker save robot-market-game:latest | gzip > robot-market-game.tar.gz
    scp robot-market-game.tar.gz docker-compose.yml user@server:~/game/
    ssh user@server 'cd ~/game && gunzip -c robot-market-game.tar.gz | docker load && docker compose up -d --no-build'
    

CI/CD روی Gitea

مخزن اصلی ssh://git@git.my-rm.com:2222/Hamed/vending-machine-game.git با نام origin است. GitHub با نام github باقی مانده. گردش‌کار .gitea/workflows/ci-cd.yml با هر push خودش اجرا می‌شود:

  • هر push و pull request: نصب، تست‌ها و build
  • push به main: بعد از قبولی تست‌ها، کد از راه SSH به سرور فرستاده می‌شود و docker compose up -d --build اجرا می‌شود.

راه‌اندازی یک‌باره:

  1. در سرور، Docker و Docker Compose نصب باشد و کاربر SSH بتواند docker اجرا کند (عضو گروه docker یا root).
  2. یک کلید SSH مخصوص انتشار بسازید و کلید عمومی را به ~/.ssh/authorized_keys سرور اضافه کنید:
    ssh-keygen -t ed25519 -N '' -f deploy_key -C gitea-deploy
    ssh-copy-id -i deploy_key.pub user@server
    
  3. در Gitea، بخش Settings → Actions → Secrets مخزن، این‌ها را تعریف کنید:
    • DEPLOY_HOST: آدرس سرور
    • DEPLOY_SSH_KEY: محتوای فایل deploy_key
    • در صورت نیاز: DEPLOY_USER (پیش‌فرض root)، DEPLOY_PORT (پیش‌فرض ۲۲) و DEPLOY_KNOWN_HOSTS (خروجی ssh-keyscan server)
  4. اختیاری، در بخش Variables:
    • DEPLOY_PATH: پوشهٔ روی سرور
    • SITE_ADDRESS: دامنه‌ها
    • HEALTH_URL: مثلاً https://robotmarkets.ir/، تا بعد از انتشار بالا آمدن سایت چک شود
    • NPM_REGISTRY: اگر runner یا سرور به registry.npmjs.org دسترسی ندارد (معمول در سرورهای ایران)، آدرس یک mirror در دسترس npm را اینجا بگذارید. همین مقدار برای build روی سرور هم استفاده می‌شود.

تا وقتی DEPLOY_HOST تعریف نشده، مرحلهٔ انتشار رد می‌شود و فقط تست و build اجرا می‌شوند. هر مرحله محدودیت زمانی دارد (تست ۱۵ دقیقه، انتشار ۳۰ دقیقه) تا runner هیچ‌وقت بی‌پایان معطل نماند.

ساخت دوبارهٔ دادهٔ نقشه

src/data/mellat.json و src/data/iran.json از خروجی خام OpenStreetMap (Overpass API) و Natural Earth ساخته می‌شوند. کوئری‌ها در ابتدای اسکریپت آمده‌اند:

node scripts/build-map-data.mjs --osm city.json zone-factory.json corridor.json major.json --poi poi.json --provinces ne_10m_admin_1_states_provinces.geojson

چهار خروجی OSM (محدودهٔ شهر، محدودهٔ کارخانه، کریدور مسیر و خیابان‌های اصلی) با هم ادغام می‌شوند و عنصرهای تکراری حذف می‌شوند. خیابان‌ها هم در مرز منطقه بریده می‌شوند. برای افزودن محدودهٔ جدید، یک خروجی دیگر به --osm اضافه کنید و در صورت نیاز REGION را در اسکریپت بزرگ‌تر کنید.

ساختار

  • src/main.js: صحنه، دوربین سوم‌شخص، کنترل‌ها، رابط کاربری (HUD)، نقشه، خرید و حلقهٔ بازی
  • src/city.js: ساخت شهر سه‌بعدی از داده‌ها (خیابان، پیاده‌رو، ساختمان، پارک، درخت، ورودی مترو، کوه‌ها) در تکه‌های ۲ کیلومتری که دورها پنهان می‌شوند
  • src/map-render.js: نقشهٔ گوشه، نقشهٔ کامل شهر (کاشی‌های کش‌شده و نمای کلی) و نقشهٔ ایران
  • src/routing.js: گراف خیابان‌ها، مسیریابی A* و پیدا کردن نام خیابان
  • src/collision.js: برخورد با ساختمان‌ها برای شخصیت‌ها، ماشین و دوربین
  • src/locations.js: دستگاه‌ها، آدرس‌ها، ساعت کاری و محصولات
  • src/game-state.js، src/payment.js، src/scoring.js: پول، خرید، پرداخت، امتیاز، درجه و مأموریت‌ها
  • src/characters.js: پدر و فرزند، و روبات راهنما که از دستگاه‌ها بیرون می‌آید
  • src/factory.js: محوطهٔ کارخانه، شامل سالن تولید، خط مونتاژ، دفتر، حیاط و تابلوها
  • src/pwa.js و public/manifest.webmanifest: نصب، کار آفلاین و پیام به‌روزرسانی
  • src/realworld.js: GPS، قطب‌نما، نرم‌سازی و حالت پایدار موقعیت، و شعاع رسیدن
  • src/ar.js: نمای دوربین (AR)، شامل تصویر دوربین پشت گوشی، جهت گوشی و زاویهٔ دید
  • src/vending.js، src/vehicle.js، src/traffic.js: مدل دستگاه، فیزیک ماشین و ترافیک شهری
  • src/geo.js: تبدیل طول و عرض جغرافیایی به مختصات بازی

محدودیت‌ها و گام‌های بعدی

  • مدل‌ها هندسی و سبک‌اند و کیفیت گرافیکی بازی‌های AAA را ندارند.
  • خیابان‌ها یک‌طرفه و چراغ راهنمایی ندارند، و ترافیک فقط روی بلوارهای اصلی است.
  • پیشنهادهای بعدی: امتیاز ایمنی (خط عابر و چراغ قرمز)، تاکسی و قطار شهری، چرخهٔ روز و شب، ذخیرهٔ پیشرفت، جدول امتیاز آنلاین، و محله‌ها و شهرهای دیگر.

نقشه: © مشارکت‌کنندگان OpenStreetMap (ODbL). مرز استان‌ها: Natural Earth.