展示HN:39C3的1D乒乓游戏
Show HN: 1D-Pong Game at 39C3

原始链接: https://github.com/ogermer/1d-pong

## 1D Pong:一款快节奏LED反应游戏 1D Pong是一款使用ESP32微控制器和55个LED WS2812B灯条构建的两人反应游戏。玩家通过在正确的时间按下按钮,将“球”(移动的白色LED)击入对方区域来竞争。 游戏玩法动态:每个得分后区域缩小,增加难度,并且在“球”进入你的区域时击打它会获得速度加成。首先达到5分的玩家获胜。游戏具有吸引模式,包含13种可定制的动画,在空闲时显示。 **主要特点:** * **快节奏、两人对战。** * **动态难度**,区域缩小。 * **提前击打奖励**,增加速度。 * **模块化动画系统**,易于定制。 * **可配置参数**,如区域大小和获胜分数。 该项目是开源的(MIT License),并鼓励贡献,建议包括添加音效、网络界面和单人模式。详细的设置说明、组件规格和代码可在GitHub上找到:[https://github.com/yourusername/1d-pong](https://github.com/yourusername/1d-pong)。

一位名为oger的开发者为39C3混沌通信大会创建了一个简单的1D-Pong游戏,作为一项有趣的个人项目,灵感来自去年活动的类似游戏。这款游戏很受欢迎,甚至在39C3 Hackaday播客中被介绍。 Oger利用这个项目测试了AI编码助手Claude Code在他现有代码库中的表现。他报告说体验非常流畅,没有错误,并对该工具的能力印象深刻。 代码可在GitHub上找到(github.com/ogermer),oger鼓励其他人在此基础上进行构建,并建议可以创建一个联网的1D-Pong联赛。他强调了游戏的表面上简单的性质和不断增加的难度是其乐趣的关键。
相关文章

原文

A two-player reaction game played on a single strip of WS2812B LEDs. Players compete by pressing their button at the right moment when the ball enters their zone.

  • Fast-paced two-player gameplay
  • Dynamic difficulty: zones shrink after each point
  • Early-hit bonus: hit the ball as it enters your zone for extra speed
  • 13 attract mode animations to draw players in
  • Modular animation system for easy customization
  • Visual feedback for hits, misses, and scoring
  • Configurable game parameters
Component Specification Quantity
Microcontroller WEMOS D1 Mini ESP32 1
LED Strip WS2812B, 55 LEDs 1
Buttons Momentary push button 2
Power Supply 5V, 3A recommended 1
Wires Jumper wires As needed

Schematic Diagram

Download PDF Schematic

Component ESP32 Pin
LED Data GPIO 5
Left Button GPIO 17
Right Button GPIO 18
Left Button LED GPIO 25 (PWM)
Right Button LED GPIO 26 (PWM)
LED Power 5V
LED Ground GND
Buttons GND (active LOW with internal pull-up)

Project Photo

  1. Clone the repository:

    git clone https://github.com/yourusername/1d-pong.git
    cd 1d-pong
  2. Open the project in VS Code:

  3. Build the project:

    • Click the checkmark icon in the PlatformIO toolbar, or
    • Press Ctrl+Alt+B
  4. Upload to your ESP32:

    • Connect your ESP32 via USB
    • Click the arrow icon in the PlatformIO toolbar, or
    • Press Ctrl+Alt+U

Edit include/config.h to customize:

// Hardware
#define NUM_LEDS            55      // Number of LEDs in your strip
#define LED_PIN             5       // Data pin for LED strip
#define BUTTON_LEFT_PIN     17      // Left player button
#define BUTTON_RIGHT_PIN    18      // Right player button

// Button LEDs (PWM capable pins)
#define BUTTON_LED_LEFT_PIN  25     // Left button LED
#define BUTTON_LED_RIGHT_PIN 26     // Right button LED
#define BUTTON_LED_PWM_FREQ  5000   // 5kHz PWM frequency
#define BUTTON_LED_PWM_RES   8      // 8-bit resolution (0-255)

// Game Parameters
#define ZONE_SIZE_START     10      // Initial zone size (LEDs)
#define ZONE_SIZE_MIN       5       // Minimum zone size
#define SCORE_TO_WIN        5       // Points to win a match

// Speed
#define BALL_INITIAL_DELAY_MS  60   // Starting ball speed (lower = faster)

// Animation
#define ANIMATION_DURATION_MS  10000UL  // Duration per animation (ms)
  1. When idle, the LED strip displays attract mode animations
  2. Either player presses their button to start a match
  3. The ball spawns in the center after a countdown
  1. The ball (white LED) travels toward one player's zone
  2. When the ball enters your zone (colored section), press your button to return it
  3. The ball speeds up with each successful return
  4. Score a point when your opponent misses
  • Miss: Ball exits the strip = opponent scores
  • Early press: Pressing outside your zone = opponent scores
  • First to 5 points wins the match
  • Early-hit bonus: Hitting the ball as it enters your zone (not waiting until it's about to exit) adds extra speed, making it harder for your opponent
  • Shrinking zones: After each point, both zones shrink by 1 LED, increasing difficulty
stateDiagram-v2
    [*] --> IDLE
    IDLE --> SERVE : Button Press
    SERVE --> BALL_MOVING : Countdown Complete
    BALL_MOVING --> BALL_MOVING : Successful Hit
    BALL_MOVING --> CHECK_GAME_OVER : Miss or Penalty
    CHECK_GAME_OVER --> SERVE : Score < 5
    CHECK_GAME_OVER --> GAME_OVER : Score ≥ 5
    GAME_OVER --> IDLE : Win Animation Complete

    note right of IDLE
        Attract mode animations
        Button LED breathing & pulses
    end note

    note right of SERVE
        Countdown with visual pulse
        Zone shrinks after each point
    end note

    note right of BALL_MOVING
        Active gameplay
        Button LEDs indicate active zones
        Flash on hit, blink on miss
    end note

    note right of GAME_OVER
        Display winner animation
        First to 5 points wins
    end note
Loading
State Description
IDLE Attract mode - cycling through animations, waiting for player
SERVE Countdown before ball launch, zones displayed
BALL_MOVING Active gameplay - ball moving, checking for hits
CHECK_GAME_OVER Evaluate if a player has won
GAME_OVER Display winner animation, then return to idle

The animation system uses auto-registration - simply create a new .cpp file in src/animations/ and it will be automatically included.

  1. Copy the template:

    cp src/animations/_template.cpp.example src/animations/my_animation.cpp
  2. Edit your new file:

    #include "animation.h"
    
    class MyAnimation : public Animation {
    public:
        MyAnimation(const char* name) : Animation(name) {}
    
        void reset() override {
            // Initialize state when animation starts
            _lastUpdate = 0;
        }
    
        void update(CRGB* leds, uint8_t numLeds) override {
            // Non-blocking timing
            if (millis() - _lastUpdate < 50) return;
            _lastUpdate = millis();
    
            // Your animation logic here
            fill_solid(leds, numLeds, CRGB::Black);
            // ...
    
            FastLED.show();
        }
    
    private:
        uint32_t _lastUpdate = 0;
    };
    
    // Register with a display name
    REGISTER_ANIMATION(MyAnimation, "My Animation");
  3. Build and upload - your animation is now in the rotation!

  • Non-blocking: Never use delay() - use millis() for timing
  • Call FastLED.show(): Required to display your changes
  • Use reset(): Initialize state variables when animation starts
  • Available helpers: fill_solid(), CHSV(), sin8(), qadd8(), qsub8(), etc.
Animation Description
Rainbow Dot Rainbow background with bouncing white dot
Pong Demo Simulated game demonstration
Plasma Comet Plasma background with white comet trail
Cylon Scanning red eye effect
Fire Flames from both player zones
Sparkle Twinkling stars with shooting stars
Duel Chase Blue/green dots colliding
Heartbeat Pulsing red heartbeat
Ocean Wave Blue sine waves with foam
Bouncing Balls Physics-based bouncing balls
Matrix Rain Green digital rain
Lightning Storm Random flashes with thunder
Color Breathing Slow color pulsing
  • Check power supply voltage (5V) and amperage (3A recommended for 55 LEDs)
  • Verify data pin connection (GPIO 5 by default)
  • Ensure correct LED type in config (WS2812B)
  • Check color order setting (GRB for most WS2812B strips)
  • Verify button connections to correct GPIO pins
  • Buttons should connect between GPIO and GND (uses internal pull-up)
  • Check for loose connections
  • Try holding BOOT button on ESP32 while uploading
  • Check USB cable (some are power-only)
  • Verify correct board selected in platformio.ini
  • Adjust BALL_INITIAL_DELAY_MS in config.h
  • Higher values = slower ball
  • Recommended range: 40-80ms
1d-pong/
├── platformio.ini          # PlatformIO configuration
├── include/
│   ├── config.h            # Game and hardware configuration
│   └── animation.h         # Animation base class
├── src/
│   ├── main.cpp            # Game logic and state machine
│   ├── animation.cpp       # Animation manager
│   └── animations/         # Animation implementations
│       ├── _template.cpp.example
│       ├── rainbow_dot.cpp
│       ├── fire.cpp
│       └── ...
└── README.md

This project was developed with assistance from AI tools (Claude, ChatGPT, etc.). All code has been reviewed and tested by the author.

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-animation)
  3. Commit your changes (git commit -m 'Add amazing animation')
  4. Push to the branch (git push origin feature/amazing-animation)
  5. Open a Pull Request
  • New attract mode animations
  • Sound effects support (buzzer/speaker)
  • Web-based configuration interface
  • Score persistence (EEPROM)
  • Tournament mode (best of N matches)
  • Single-player mode with AI

This project was inspired by a 1D Pong game seen at 38C3 (38th Chaos Communication Congress).

Built with:

This project is licensed under the MIT License - see below for details.

MIT License

Copyright (c) 2025

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
联系我们 contact @ memedata.com