
برمجة لعبة إطلاق نار باستخدام لغة Rust: دليل شامل للمبتدئين
لغة Rust هي #لغة_برمجة حديثة المعروفة بأدائها الفائق وميزاتها الأمنية القوية،
تبرز كخيار مثالي لتطوير ألعاب إطلاق النار التي تتطلب دقة وسرعة.
في هذا الدليل، سنأخذك في رحلة خطوة بخطوة لإنشاء لعبة إطلاق نار باستخدام #Rust،
مستعرضين كيفية بناء العناصر الأساسية للعبة، من الحركة والتصويب إلى آليات إطلاق النار والتفاعل مع البيئة.
سنقوم بإنشاء لعبة إطلاق نار باستخدام لغة Rust ومكتبة ggez،
وسنغطي الخطوات الأساسية لإنشاء اللعبة، بدءًا من إعداد البيئة وحتى إضافة وظائف إطلاق النار والحركة.
#برمجة_الألعاب_باستخدام_Rust_وggez #لعبة_إطلاق_نار #برمجة_ألعاب
#ألعاب_ثنائية_الأبعاد
خطوات برمجة لعبة إطلاق نار باستخدام لغة Rust
في هذا القسم، نركز على الجانب العملي لتطوير لعبة إطلاق نار باستخدام Rust.
سنقوم بتفصيل الخطوات اللازمة لتحويل الأفكار والتصميمات إلى واقع برمجي،
مع التركيز على استخدام ميزات Rust الفريدة لتحقيق أداء عالٍ وتجربة لعب سلسة.
سنتناول كيفية إنشاء بيئة اللعب، وتطبيق آليات الحركة، وتنفيذ أنظمة إطلاق النار، وتطوير
الذكاء الاصطناعي للأعداء، وإضافة المؤثرات البصرية والصوتية، اليك التفاصيل :
الخطوة 1: إعداد البيئة وتثبيت المكتبات
قبل البدء، تأكد من تثبيت Rust على جهازك، ثم قم بإنشاء
مشروع جديد باستخدام Cargo :
cargo new shooting_gamecd shooting_game
* أضف مكتبة ggez إلى ملف Cargo.toml :
Ini, TOML
[dependencies]
ggez = "0.8"
rand = "0.8"
--
الخطوة 2: إنشاء نافذة اللعبة
أولاً، سنقوم بإنشاء نافذة اللعبة باستخدام ggez.
إليك الكود في ملف src/main.rs :
use ggez::{Context, ContextBuilder, GameResult, event, graphics, timer};use ggez::event::{EventHandler, KeyCode, KeyMods};use ggez::graphics::{Color, Mesh, MeshBuilder, DrawParam};use ggez::nalgebra as na;use rand::{thread_rng, Rng};
struct MainState { player_pos: na::Point2<f32>, enemy_pos: na::Point2<f32>, bullet_pos: na::Point2<f32>, bullet_state: BulletState, score: i32,}
enum BulletState { Ready, Firing,}
impl MainState { fn new(ctx: &mut Context) -> GameResult<MainState> { let s = MainState { player_pos: na::Point2::new(400.0, 550.0), enemy_pos: na::Point2::new(thread_rng().gen_range(50.0..750.0), 50.0), bullet_pos: na::Point2::new(0.0, 550.0), bullet_state: BulletState::Ready, score: 0, }; Ok(s) }}
impl EventHandler<ggez::GameError> for MainState { fn update(&mut self, ctx: &mut Context) -> GameResult { // تحديث منطق اللعبة Ok(()) }
fn draw(&mut self, ctx: &mut Context) -> GameResult { graphics::clear(ctx, Color::BLACK);
// رسم اللاعب والعدو والرصاصة let player_mesh = MeshBuilder::new().circle(graphics::DrawMode::fill(), self.player_pos, 25.0, 0.1, Color::GREEN)?.build(ctx)?; graphics::draw(ctx, &player_mesh, DrawParam::default())?;
let enemy_mesh = MeshBuilder::new().circle(graphics::DrawMode::fill(), self.enemy_pos, 25.0, 0.1, Color::RED)?.build(ctx)?; graphics::draw(ctx, &enemy_mesh, DrawParam::default())?;
if let BulletState::Firing = self.bullet_state { let bullet_mesh = MeshBuilder::new().circle(graphics::DrawMode::fill(), self.bullet_pos, 10.0, 0.1, Color::YELLOW)?.build(ctx)?; graphics::draw(ctx, &bullet_mesh, DrawParam::default())?; }
graphics::present(ctx)?; timer::yield_now(); Ok(()) }
fn key_down_event(&mut self, ctx: &mut Context, keycode: KeyCode, _keymods: KeyMods, _repeat: bool) { match keycode { KeyCode::Space => { if let BulletState::Ready = self.bullet_state { self.bullet_pos = self.player_pos; self.bullet_state = BulletState::Firing; } } _ => {} } }}
fn main() -> GameResult { let (mut ctx, event_loop) = ContextBuilder::new("shooting_game", "Your Name") .build()?; let state = MainState::new(&mut ctx)?; event::run(ctx, event_loop, state)}
* شرح الكود :
- ggez::init(): تهيئة ggez.
- ContextBuilder::new(): إنشاء نافذة اللعبة وتحديد أبعادها.
- MainState: هيكل لتخزين حالة اللعبة.
- EventHandler: معالجة الأحداث التي وقعت.
- graphics::clear(): مسح الشاشة.
- graphics::draw(): رسم العناصر على الشاشة.
- graphics::present(): تحديث الشاشة.
- timer::yield_now(): إعطاء وقت للمعالج.
- KeyCode::Space: التحقق من الضغط على مفتاح المسافة.
الخطوة 3: إضافة اللاعب والعدو
تم بالفعل إضافة اللاعب والعدو في الكود السابق.
** مقالات ذات صلة :
الخطوة 4: إضافة حركة اللاعب والعدو
قم بتعديل دالة update في impl EventHandler لإضافة حركة اللاعب والعدو :
fn update(&mut self, ctx: &mut Context) -> GameResult { let dt = timer::delta(ctx).as_secs_f32();
// حركة اللاعب let keys = event::pressed_keys(ctx); if keys.contains(&KeyCode::Left) { self.player_pos.x -= 300.0 * dt; } if keys.contains(&KeyCode::Right) { self.player_pos.x += 300.0 * dt; }
// حركة العدو self.enemy_pos.x += 150.0 * dt; if self.enemy_pos.x < 50.0 || self.enemy_pos.x > 750.0 { self.enemy_pos.x = self.enemy_pos.x.clamp(50.0, 750.0); self.enemy_pos.y += 50.0; }
// حركة الرصاصة if let BulletState::Firing = self.bullet_state { self.bullet_pos.y -= 500.0 * dt; if self.bullet_pos.y < 0.0 { self.bullet_state = BulletState::Ready; } }
Ok(())}
الخطوة 5: إضافة الرصاصة
تم بالفعل إضافة الرصاصة في الكود السابق.
الخطوة 6: إضافة التصادم
قم بتعديل دالة update لإضافة وظيفة التصادم :
fn update(&mut self, ctx: &mut Context) -> GameResult { // ... (الكود السابق)
// التحقق من التصادم if let BulletState::Firing = self.bullet_state { let distance = na::distance(&self.bullet_pos, &self.enemy_pos); if distance < 35.0 { self.bullet_state = BulletState::Ready; self.enemy_pos = na::Point2::new(thread_rng().gen_range(50.0..750.0), 50.0); self.score += 1; } }
Ok(())}
الخطوة 7: إضافة نقاط اللعبة
قم بتعديل دالة draw لعرض النقاط :
fn draw(&mut self, ctx: &mut Context) -> GameResult { // ... (الكود السابق)
let score_text = graphics::Text::new(format!("Score: {}", self.score)); graphics::draw(ctx, &score_text, DrawParam::new().dest(na::Point2::new(10.0, 10.0)))?;
graphics::present(ctx)?; timer::yield_now(); Ok(())}
الخطوة 8: إضافة التصادم
قم بتعديل دالة update لإضافة وظيفة التصادم :
fn update(&mut self, ctx: &mut Context) -> GameResult { // ... (الكود السابق)
// التحقق من التصادم if let BulletState::Firing = self.bullet_state { let distance = na::distance(&self.bullet_pos, &self.enemy_pos); if distance < 35.0 { self.bullet_state = BulletState::Ready; self.enemy_pos = na::Point2::new(thread_rng().gen_range(50.0..750.0), 50.0); self.score += 1; } }
Ok(())}
* شرح الكود :
- na::distance(&self.bullet_pos, &self.enemy_pos):
حساب المسافة بين الرصاصة والعدو باستخدام مكتبة nalgebra.
- distance < 35.0: التحقق من أن المسافة بين الرصاصة والعدو أقل من 35 بكسل.
- في حالة التصادم، يتم إعادة تهيئة موقع العدو، وزيادة النقاط، وتغيير حالة الرصاصة إلى "جاهز".
الخطوة 9: إضافة نقاط اللعبة
قم بتعديل دالة draw لعرض النقاط :
fn draw(&mut self, ctx: &mut Context) -> GameResult { // ... (الكود السابق)
let score_text = graphics::Text::new(format!("Score: {}", self.score)); graphics::draw(ctx, &score_text, DrawParam::new().dest(na::Point2::new(10.0, 10.0)))?;
graphics::present(ctx)?; timer::yield_now(); Ok(())}
* شرح الكود :
- graphics::Text::new(format!("Score: {}", self.score)):
إنشاء نص لعرض النقاط.
- graphics::draw(ctx, &score_text, DrawParam::new().dest(na::Point2::new(10.0, 10.0))):
رسم النص على الشاشة.
الخطوة 10: إضافة نهاية اللعبة
قم بتعديل دالة update لإضافة شاشة نهاية اللعبة عند خسارة اللاعب :
fn update(&mut self, ctx: &mut Context) -> GameResult { // ... (الكود السابق)
// التحقق من نهاية اللعبة if self.enemy_pos.y > 550.0 { // شاشة نهاية اللعبة self.player_pos = na::Point2::new(400.0, 550.0); self.enemy_pos = na::Point2::new(thread_rng().gen_range(50.0..750.0), 50.0); self.bullet_pos = na::Point2::new(0.0, 550.0); self.bullet_state = BulletState::Ready; self.score = 0; }
Ok(())}
في حالة تجاوز العدو لحدود الشاشة، يتم إعادة تهيئة مواقع اللاعب و
العدو والرصاصة، وإعادة تعيين النقاط إلى الصفر.
الخطوة 11: إضافة مؤثرات صوتية (اختياري)
يمكنك إضافة مؤثرات صوتية لجعل اللعبة أكثر جاذبية. لتنفيذ ذلك، ستحتاج إلى
إضافة مكتبة ggez::audio وتضمين ملفات صوتية في مشروعك.
* أضف مكتبة ggez::audio إلى ملف Cargo.toml :
Ini, TOML
[dependencies]
ggez = { version = "0.8", features = ["audio"] }
rand = "0.8"
--
* قم بإنشاء مجلد resources في جذر مشروعك وضع فيه ملفات الصوت.
* قم بتحميل وتشغيل الأصوات في الكود :
use ggez::audio::{Source, SoundData};
// ... (في هيكل MainState)struct MainState { // ... shoot_sound: Source, hit_sound: Source,}
impl MainState { fn new(ctx: &mut Context) -> GameResult<MainState> { let shoot_sound_data = SoundData::from_path(ctx, "/shoot.wav")?; let shoot_sound = Source::from_data(ctx, shoot_sound_data)?;
let hit_sound_data = SoundData::from_path(ctx, "/hit.wav")?; let hit_sound = Source::from_data(ctx, hit_sound_data)?;
let s = MainState { // ... shoot_sound, hit_sound, }; Ok(s) }}
// ... (في دالة key_down_event)KeyCode::Space => { if let BulletState::Ready = self.bullet_state { self.bullet_pos = self.player_pos; self.bullet_state = BulletState::Firing; self.shoot_sound.play(ctx)?; }}
// ... (في دالة update، داخل شرط التصادم)if distance < 35.0 { // ... self.hit_sound.play(ctx)?;}
الخطوة 12: إضافة صور (اختياري)
يمكنك إضافة صور لشخصيات اللعبة والخلفية لجعلها أكثر جاذبية.
قم بإنشاء مجلد resources في جذر مشروعك وضع فيه ملفات الصور.
* قم بتحميل ورسم الصور في الكود :
use ggez::graphics::{Image, ImageGeneric};// ... (في هيكل MainState)struct MainState {// ...player_image: Image,enemy_image: Image,bullet_image: Image,}impl MainState {fn new(ctx: &mut Context) -> GameResult<MainState> {let player_image = Image::from_path(ctx, "/player.png")?;let enemy_image = Image::from_path(ctx, "/enemy.png")?;let bullet_image = Image::from_path(ctx, "/bullet.png")?;let s = MainState {// ...player_image,enemy_image,bullet_image,};Ok(s)}}// ... (في دالة draw، استبدل رسم الدوائر برسم الصور)graphics::draw(ctx, &self.player_image, DrawParam::new().dest(self.player_pos))?;graphics::draw(ctx, &self.enemy_image, DrawParam::new().dest(self.enemy_pos))?;if let BulletState::Firing = self.bullet_state {graphics::draw(ctx, &self.bullet_image, DrawParam::new().dest(self.bullet_pos))?;}
* الخاتمة :
في هذا المقال، قمنا بإنشاء لعبة إطلاق نار بسيطة باستخدام لغة Rust ومكتبة ggez.
يمكنك استخدام هذا التطبيق كنقطة بداية لإنشاء ألعاب أكثر تعقيدًا.
يمكنك إضافة المزيد من الميزات إلى اللعبة، مثل أنواع مختلفة من الأعداء، ومستويات
صعوبة مختلفة، والمزيد من المؤثرات الصوتية والبصرية.