// GDnD wiki example // Demonstrates: keyboard input, clamped movement, and a one-line "track the ball" AI for the second paddle // Related pages: [[overview-arcade-classics-as-learning-projects]], [[unity-input]], [[unity-transform]] using UnityEngine; // One script for both paddles. Set controlMode in the Inspector: Player reads an // input axis; Ai chases the ball's height. The Ai branch is the seed of every // later "enemy follows target" behaviour in the coding progression. public class Paddle2D : MonoBehaviour { public enum ControlMode { Player, Ai } [SerializeField] private ControlMode controlMode = ControlMode.Player; [SerializeField] private float speed = 12f; [Header("Player")] [Tooltip("Vertical input axis name (e.g. \"Vertical\" or a custom second-player axis).")] [SerializeField] private string verticalAxis = "Vertical"; [Header("AI")] [SerializeField] private Transform ball; [Tooltip("Caps AI reaction so it is beatable. Lower = lazier paddle.")] [SerializeField, Range(0f, 1f)] private float aiResponsiveness = 0.65f; private float halfHeight; private float halfSize; private Camera cachedCamera; private void Awake() { cachedCamera = Camera.main; halfSize = GetComponent().bounds.extents.y; } private void Update() { float move = controlMode == ControlMode.Player ? ReadPlayerInput() : ReadAiInput(); Vector3 position = transform.position; position.y += move * speed * Time.deltaTime; halfHeight = cachedCamera.orthographicSize - halfSize; position.y = Mathf.Clamp(position.y, -halfHeight, halfHeight); transform.position = position; } private float ReadPlayerInput() { return Input.GetAxisRaw(verticalAxis); } private float ReadAiInput() { if (ball == null) { return 0f; } // Move toward the ball, but scale by responsiveness so the AI lags and // the player can win. A deadzone stops jitter when roughly aligned. float difference = ball.position.y - transform.position.y; if (Mathf.Abs(difference) < 0.1f) { return 0f; } return Mathf.Sign(difference) * aiResponsiveness; } }