Rust 实现投篮小游戏:控制台版「三分投小投手」
一、游戏简介
这是一款基于 Rust 语言开发的控制台投篮小游戏。玩家将扮演篮球运动员,通过输入角度和力度控制控制投篮,挑战在限定时间内投中更多球。游戏核心机制包括物理引擎模拟、随机风速干扰、计分系统和难度递增,所有逻辑封装在单一结构体中,适合 Rust 初学者学习控制台交互与物理模拟基础。
二、核心功能
五、玩法攻略
六、Rust 特性应用解析
七、扩展方向
这款投篮小游戏不仅是对 Rust 基础语法的实践,更融合了物理模拟、用户交互和游戏设计思想。通过调整物理参数(如重力、空气阻力),可以模拟不同环境下的投篮体验(例如「月球模式」低重力),为游戏扩展提供了广阔空间。对于 Rust 初学者而言,这是一个理解结构体设计、数值计算和控制台交互的绝佳
- 物理模拟:基于抛射运动公式计算篮球轨迹,考虑角度、初速度和风速影响
- 随机干扰:每轮随机生成风速,影响投篮精度
- 难度递增:随得分增加,投篮距离逐渐变远
- 计时系统:每轮有 10 秒决策时间,超时自动判定失败
- 状态展示:实时显示抛物线轨迹、剩余时间和历史成绩
use std::collections::VecDeque; use std::io; use std::time::{Duration, Instant}; use rand::Rng as _; use rand::thread_rng; // 游戏状态结构体(单类封装所有逻辑) struct BasketballGame { score: i32, // 当前得分 high_score: i32, // 最高分 distance: f64, // 投篮距离(米) wind: f64, // 风速(影响水平偏移,-2.0到2.0) history: VecDeque<String>,// 历史记录(最近5条) game_over: bool, // 游戏是否结束 } impl BasketballGame { // 初始化游戏 fn new() -> Self { BasketballGame { score: 0, high_score: 0, distance: 5.0, // 初始距离5米(NBA三分线约7.25米) wind: 0.0, history: VecDeque::with_capacity(5), game_over: false, } } // 显示游戏状态 fn show_status(&self) { println!("\n===== 篮球投篮游戏 ====="); println!("当前得分: {} | 最高分: {}", self.score, self.high_score); println!("投篮距离: {:.1}米 | 风速: {:.1}m/s (左负右正)", self.distance, self.wind); println!("-------------------------"); println!("历史记录:"); if self.history.is_empty() { println!(" 暂无记录"); } else { for record in &self.history { println!(" {}", record); } } println!("========================="); } // 生成随机风速 fn generate_wind(&mut self) { let mut rng = thread_rng(); self.wind = rng.random_range(-2.0..=2.0); // 风速范围:-2到2m/s } // 计算投篮结果(物理模拟) fn calculate_result(&self, angle: f64, velocity: f64) -> (bool, f64) { // 角度转弧度 let rad = angle.to_radians(); // 分解速度分量 let vx = velocity * rad.cos(); // 水平速度 let vy = velocity * rad.sin(); // 垂直速度 // 重力加速度(m/s²) const GRAVITY: f64 = 9.8; // 篮球直径(约0.24米,用于判定命中范围) const BALL_SIZE: f64 = 0.24; // 篮筐直径(约0.45米) const HOOP_SIZE: f64 = 0.45; // 飞行时间(垂直方向速度减为0的时间) let time_to_peak = vy / GRAVITY; // 最大高度 let peak_height = vy * time_to_peak - 0.5 * GRAVITY * time_to_peak.powi(2); // 到达篮筐水平距离所需时间(考虑风速影响) // 风速会导致水平加速度变化(简化模型:wind * 0.1) let horizontal_accel = self.wind * 0.1; // 水平方向运动方程:distance = vx * t + 0.5 * horizontal_accel * t² // 解二次方程 a*t² + b*t + c = 0 let a = 0.5 * horizontal_accel; let b = vx; let c = -self.distance; let discriminant = b.powi(2) - 4.0 * a * c; if discriminant < 0.0 { return (false, 1000.0); // 无法到达篮筐距离 } let t1 = (-b + discriminant.sqrt()) / (2.0 * a); let t2 = (-b - discriminant.sqrt()) / (2.0 * a); let flight_time = if t1 > 0.0 { t1 } else { t2 }; // 到达篮筐时的高度 let height_at_hoop = vy * flight_time - 0.5 * GRAVITY * flight_time.powi(2); // 篮筐标准高度3.05米,允许±0.3米误差 let height_ok = height_at_hoop >= 2.75 && height_at_hoop <= 3.35; // 水平偏移(风速导致) let horizontal_offset = 0.5 * horizontal_accel * flight_time.powi(2); // 总偏移量(需小于篮筐和篮球的半径和) let total_offset = horizontal_offset.abs(); let offset_ok = total_offset <= (HOOP_SIZE + BALL_SIZE) / 2.0; // 命中判定 let is_scored = height_ok && offset_ok; (is_scored, peak_height) } // 显示投篮轨迹(简化版) fn show_trajectory(&self, peak_height: f64) { println!("\n投篮轨迹模拟:"); let max_rows = 10; // 轨迹显示行数 let height_ratio = max_rows as f64 / peak_height.min(10.0); // 高度缩放比例 for i in 0..=max_rows { let current_height = peak_height * (1.0 - (i as f64 / max_rows as f64 - 0.5).powi(2) * 4.0); let row_height = current_height * height_ratio; let stars = row_height.max(0.0).min(1.0) * 20.0; // 水平长度 // 绘制轨迹线 if stars > 0.0 { let stars_str = "*".repeat(stars as usize); println!(" {:^30}", stars_str); // 居中显示 } } println!(" {:^30}", "篮筐"); // 篮筐位置标记 } // 单轮游戏逻辑 fn play_round(&mut self) { self.generate_wind(); self.show_status(); println!("\n第{}轮:请输入投篮参数", self.score + 1); println!("提示:角度建议15-60度,速度建议8-15m/s(风速会影响偏移)"); // 计时开始 let start_time = Instant::now(); const TIME_LIMIT: u64 = 10; // 10秒限时 // 获取角度输入 let angle = loop { print!("请输入投篮角度(度): "); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); // 检查超时 if start_time.elapsed() > Duration::from_secs(TIME_LIMIT) { println!("\n⏰ 超时!本轮失败"); self.game_over = true; return; } match input.trim().parse::<f64>() { Ok(num) if num > 0.0 && num < 90.0 => break num, _ => println!("无效角度!请输入0-90之间的数字"), } }; // 获取速度输入 let velocity = loop { print!("请输入投篮速度(m/s): "); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); // 检查超时 if start_time.elapsed() > Duration::from_secs(TIME_LIMIT) { println!("\n⏰ 超时!本轮失败"); self.game_over = true; return; } match input.trim().parse::<f64>() { Ok(num) if num > 0.0 && num < 20.0 => break num, _ => println!("无效速度!请输入0-20之间的数字"), } }; // 计算结果 let (scored, peak_height) = self.calculate_result(angle, velocity); self.show_trajectory(peak_height); // 处理结果 if scored { self.score += 1; self.high_score = self.high_score.max(self.score); // 每得3分增加距离(难度提升) if self.score % 3 == 0 { self.distance += 0.5; self.distance = self.distance.min(8.0); // 最大距离8米 } self.history.push_back(format!("命中!角度{:.1}° 速度{:.1}m/s", angle, velocity)); println!("\n🎉 投中了!当前得分:{}", self.score); } else { self.history.push_back(format!("未中!角度{:.1}° 速度{:.1}m/s", angle, velocity)); println!("\n❌ 未投中!游戏结束"); self.game_over = true; } // 保持历史记录最多5条 if self.history.len() > 5 { self.history.pop_front(); } } // 运行游戏主循环 fn run(&mut self) { println!("===== 欢迎来到控制台投篮游戏 ====="); println!("游戏规则:"); println!("1. 每轮需输入投篮角度(0-90度)和初速度(0-20m/s)"); println!("2. 风速会影响投篮轨迹,左负右正"); println!("3. 每轮有10秒决策时间,超时失败"); println!("4. 投中得分,连续命中会增加距离提升难度"); println!("5. 未投中则游戏结束,记录最高分"); println!("----------------------------------"); println!("按Enter开始游戏..."); io::stdin().read_line(&mut String::new()).unwrap(); while !self.game_over { self.play_round(); if !self.game_over { println!("\n按Enter继续下一轮..."); io::stdin().read_line(&mut String::new()).unwrap(); } } println!("\n===== 游戏结束 ====="); println!("最终得分:{} | 最高分:{}", self.score, self.high_score); println!("感谢游玩!"); } } // 主函数 fn main() { let mut game = BasketballGame::new(); game.run(); }
-
四、代码解析
-
结构体设计
BasketballGame结构体封装了所有游戏状态:得分、距离、风速、历史记录等核心数据,通过impl块实现所有游戏逻辑(物理计算、输入处理、状态展示等),严格遵循「单一类」设计要求。 -
物理模拟核心投篮轨迹计算基于经典抛射运动公式:
- 垂直方向:受重力影响做匀减速运动(
h = v_y * t - 0.5 * g * t²) - 水平方向:受风速干扰(简化为水平加速度),通过二次方程求解飞行时间
- 命中判定:同时满足高度在篮筐范围内(2.75-3.35 米)和水平偏移量小于篮筐与篮球的半径和
- 垂直方向:受重力影响做匀减速运动(
-
交互与节奏控制
- 控制台输入采用循环验证模式,确保角度和速度在合理范围
- 10 秒限时机制通过
Instant::now()与Duration实现,超时自动判定失败 - 历史记录使用
VecDeque存储,保持最近 5 条记录,自动淘汰旧数据
-
难度递增系统初始投篮距离为 5 米(接近 NBA 三分线),每连续命中 3 次增加 0.5 米距离(最大 8 米),迫使玩家不断调整投篮参数,提升游戏挑战性。
-
基础参数参考
- 5 米距离:推荐角度 30-40 度,速度 9-11m/s
- 7 米距离:推荐角度 40-50 度,速度 11-13m/s
- 逆风(负风速):适当增加角度补偿水平偏移
- 顺风(正风速):适当减小角度避免偏移过量
-
进阶技巧
- 观察风速:风速每轮随机生成,正数向右偏移,负数向左偏移,需针对性调整角度
- 控制节奏:每轮有 10 秒时间,建议先观察风速再决定参数,避免仓促输入
- 距离适应:距离增加时,不仅要提高速度,还需增大角度以保证足够的飞行高度
-
常见问题
- 总是短距离:速度不足或角度太小,导致篮球未到达篮筐就落地
- 总是过远:速度过大或角度太大,篮球飞过篮筐
- 高度不够:角度太小,导致篮球在到达篮筐时已下落过低
-
类型安全与数值验证输入解析时使用
match语句严格验证数值范围(如角度 0-90 度),结合if守卫确保参数合法性,体现 Rust 对类型安全的严格要求。 -
结构体与方法封装游戏状态与行为通过结构体紧密绑定,例如
generate_wind方法直接修改结构体的wind字段,play_round方法统筹单轮游戏流程,符合面向对象的封装思想。 -
集合类型选择使用
VecDeque存储历史记录,利用其高效的首尾操作特性(push_back和pop_front),确保只保留最近 5 条记录,兼顾性能与内存占用。 -
时间处理通过
std::time模块实现精确计时,Instant::elapsed()实时计算输入耗时,超时机制增强了游戏的紧张感。 -
物理计算中的浮点数处理使用
f64类型进行高精度物理计算,通过powi方法处理幂运算,sqrt方法求解二次方程,展示了 Rust 对科学计算的良好支持。 - 增加多种投篮模式:如三分球大赛(固定距离限时)、障碍赛(需避开随机障碍物)
- 实现球员属性系统:不同球员有不同的力量(影响速度上限)和精准度(影响风速抗性)
- 添加音效反馈:结合
rodio库在命中 / 未中时播放对应音效 - 图形化升级:使用
tui-rs绘制更直观的轨迹动画,或用bevy引擎开发 2D 版本
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)