You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中同一KeyCode实现点击单次移动与长按延迟连续移动方案

Fixing Tetromino Horizontal Input (Single Tap vs Long Hold)

I get exactly what you're dealing with—when you tap D/A once, the block moves 2-3 times instead of just once, and the long hold doesn’t have that initial delay before repeating. The core issue is that we need to explicitly separate single tap behavior from delayed continuous hold behavior instead of relying solely on GetKeyDown and GetKey, which can overlap unexpectedly.

Here's how to refactor your Tetromino script to fix this:

Step 1: Add Input State Variables

First, add these variables at the top of your class to track the input state:

// Horizontal movement input tracking
private float horizontalHoldTimer = 0f;
private bool hasMovedOnce = false;
private bool isHoldingHorizontal = false;
public float horizontalHoldDelay = 0.5f; // Delay before continuous movement starts
public float horizontalRepeatRate = 0.1f; // Speed of continuous movement after delay
private KeyCode currentHorizontalKey; // Tracks which key (A/D) is being held

Step 2: Refactor the CheckUserInput Method

Replace your existing CheckUserInput method with this version that handles both single taps and delayed holds properly:

void CheckUserInput() {
    // Handle Horizontal Movement (Left/Right)
    HandleHorizontalInput(KeyCode.D, Vector3.right);
    HandleHorizontalInput(KeyCode.A, Vector3.left);

    // Handle Rotation (W)
    if (Input.GetKeyDown(KeyCode.W)) {
        if (allowRotation) {
            float rotationAmount = limitRotation ? 
                (transform.rotation.eulerAngles.z >= 90 ? -90f : 90f) : 90f;
            transform.Rotate(0, 0, rotationAmount);
            
            if (!CheckIsValidPosition()) {
                // Undo rotation if invalid
                transform.Rotate(0, 0, -rotationAmount);
            }
        }
    }

    // Handle Soft Drop (S)
    if (Input.GetKeyDown(KeyCode.S)) {
        MoveTetromino(Vector3.down);
    }
}

// Helper method to handle left/right input logic
void HandleHorizontalInput(KeyCode key, Vector3 direction) {
    // Key pressed down for the first time
    if (Input.GetKeyDown(key)) {
        // Move once immediately
        MoveTetromino(direction);
        // Initialize hold state
        hasMovedOnce = true;
        isHoldingHorizontal = true;
        currentHorizontalKey = key;
        horizontalHoldTimer = Time.time;
    }
    // Key is being held
    else if (Input.GetKey(key) && isHoldingHorizontal && currentHorizontalKey == key) {
        // Wait for the initial delay before repeating
        if (Time.time - horizontalHoldTimer >= horizontalHoldDelay) {
            // After delay, repeat movement at the set rate
            if (Time.time - horizontalHoldTimer >= horizontalHoldDelay + horizontalRepeatRate) {
                MoveTetromino(direction);
                horizontalHoldTimer = Time.time - horizontalHoldDelay; // Reset timer to maintain repeat rate
            }
        }
    }
    // Key released
    else if (Input.GetKeyUp(key) && currentHorizontalKey == key) {
        // Reset hold state
        isHoldingHorizontal = false;
        hasMovedOnce = false;
        currentHorizontalKey = KeyCode.None;
    }
}

// Helper method to handle tetromino movement and validation
bool MoveTetromino(Vector3 direction) {
    transform.position += direction;
    if (CheckIsValidPosition()) {
        return true;
    } else {
        // Undo movement if invalid
        transform.position -= direction;
        // If moving right/left and we hit a boundary, reset hold state
        if (direction == Vector3.right || direction == Vector3.left) {
            isHoldingHorizontal = false;
            hasMovedOnce = false;
            currentHorizontalKey = KeyCode.None;
        }
        return false;
    }
}

What This Does:

  • Single Tap: When you press D/A once, it triggers an immediate single movement, then sets up the hold state but won’t repeat until the delay passes. If you release the key before the delay, it resets everything so no extra movement happens.
  • Long Hold: Press and hold D/A—after 0.5 seconds (adjustable via horizontalHoldDelay), the block starts moving continuously at the horizontalRepeatRate speed until you release the key or hit a boundary.
  • Boundary Handling: If the block hits a wall while holding, it stops moving immediately and resets the hold state so you don’t get stuck trying to move further.

Optional Tweaks:

  • Adjust horizontalHoldDelay to make the initial wait shorter/longer (0.5s is standard for most Tetris games).
  • Adjust horizontalRepeatRate to make the continuous movement faster/slower.
  • The MoveTetromino helper cleans up duplicate code for movement validation, making the script easier to maintain.

Notes on Your Original Code:

I also cleaned up the rotation logic to avoid duplicate code, but kept your original allowRotation and limitRotation behavior intact. The soft drop (S key) works exactly as before.

内容的提问来源于stack exchange,提问作者Mathieu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:47:40