القائمة الرئيسية

الصفحات

برمجة لعبة اطلاق نار ثنائية الأبعاد باستخدام Kotlin

Android APK، Android AAB، Google Play Store، Android App Publishing، Kotlin App Deployment، Kotlin SharedPreferences، Android Data Persistence، Kotlin Save Game State، Kotlin Touch Event Handling، Android OnTouchListener، Kotlin Input، Kotlin Collision Detection، Kotlin RectF، Kotlin Game Score، Kotlin Game Logic، Kotlin Player Movement، Kotlin Enemy AI، Kotlin Bullet Movement، Kotlin، Game ،Kotlin Game Objec، Kotlin Sprite، Kotlin Player، Kotlin Enemy، Kotlin Bullet، Kotlin Vector2، Kotlin SurfaceView، Kotlin Canvas، Kotlin Paint، Kotlin Game Loop، Kotlin Surface Holder Callback، Android Studio، إنشاء مشروع Kotlin، How to Code a 2D Shooter Game Using Kotlin، How-to-Code-2D-Shooter-Game-in-Kotlin، برمجة ألعاب Kotlin، تطوير ألعاب Android Kotlin، لعبة إطلاق نار Kotlin، Kotlin 2D Game، Android Game Development Kotlin، حفظ نقاط اللعبة Kotlin، نشر تطبيقات Android Kotlin، واجهة مستخدم لعبة Kotlin، Game Loop Kotlin، Sprite Handling Kotlin، Collision Detection Kotlin، برمجة لعبة إطلاق نار باستخدام لغة كوتلن، كيفية برمجة لعبة اطلاق نار ثنائية الأبعاد باستخدام Kotlin، دليل شامل مع الأكواد والخطوات، برمجة لعبة اطلاق نار ثنائية الأبعاد باستخدام Kotlin، برمجة ألعاب Kotlin، تطوير ألعاب Android Kotlin ،Kotlin 2D Game، نشر تطبيقات Android Kotlin، Kotlin Enemy AI ،Timer ،Kotlin Bitmap، Kotlin BitmapFactory، Android Resources Drawable،




برمجة لعبة اطلاق نار ثنائية الأبعاد باستخدام Kotlin 



تعتبر برمجة ألعاب إطلاق النار ثنائية الأبعاد مشروعًا مثيرًا للاهتمام 
يمكن تنفيذه باستخدام لغة Kotlin و مكتبات تطوير الألعاب المناسبة. 
في هذا المقال، سنستعرض الخطوات الأساسية لإنشاء لعبة إطلاق نار بسيطة ثنائية الأبعاد، 
بدءًا من إنشاء واجهة المستخدم، مرورًا بمنطق اللعبة الأساسي، وصولًا
 إلى حفظ النقاط ونشر التطبيق على نظام Android.
 سنقدم الأكواد الأساسية لكل خطوة مع شرح للمفاهيم الهامة.


خطوات برمجة لعبة إطلاق نار ثنائية الأبعاد باستخدام Kotlin



هل تحلم بإنشاء لعبتك الخاصة التي تثير حماس اللاعبين؟ هل تستمتع بتحدي
 برمجة الحركة والتصويب والتفاعل؟ باستخدام لغة Kotlin القوية وبيئة تطوير
 Android Studio، يمكنك تحويل هذه الأحلام إلى واقع! يعتبر تطوير
 لعبة إطلاق نار ثنائية الأبعاد مشروعًا رائعًا للمبتدئين والمطورين على حد سواء، 
حيث يجمع بين المفاهيم الأساسية لتطوير الألعاب وتجربة مستخدم ممتعة.
في هذا الدليل الشامل، سننطلق في رحلة خطوة بخطوة لإنشاء لعبة إطلاق نار
 ثنائية الأبعاد بسيطة ولكنها جذابة على نظام Android. سنبدأ بتجهيز الأدوات اللازمة،
 ثم نتعمق في بناء واجهة المستخدم التفاعلية، وتصميم آليات التحكم والاستجابة، 
وإضافة الأعداء والتحديات، وصولًا إلى حفظ إنجازات اللاعب ونشر تحفتك على متجر 
Google Play ليشاركها العالم. استعد للانغماس في عالم برمجة الألعاب واكتشاف
 الإمكانيات الإبداعية التي تتيحها لك لغة Kotlin!

* الأدوات المطلوبة :
- Android Studio : بيئة تطوير متكاملة (IDE) لتطوير تطبيقات Android بلغة Kotlin.
- Kotlin : لغة البرمجة المستخدمة.
- Android SDK : مجموعة أدوات تطوير برامج Android.
- Canvas و Paint : كلاسات أساسية في Android لرسم الرسومات ثنائية الأبعاد.
- SurfaceView : يوفر سطحًا مخصصًا للرسم المستمر، وهو ضروري لأداء الألعاب السلس.

* الخطوات الأساسية :
1. إنشاء مشروع Android جديد :
- افتح Android Studio وابدأ مشروع Android جديدًا.
- اختر "Empty Activity" كلغة Kotlin.
- قم بتسمية مشروعك وحدد موقع الحفظ.

2. إعداد واجهة المستخدم (GameView):

قم بإنشاء كلاس Kotlin جديد باسم GameView يرث من
 SurfaceView وينفذ SurfaceHolder.Callback.
في هذا الكلاس، سنقوم بإدارة سطح الرسم (Canvas) وحلقة اللعبة :


        

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.view.SurfaceHolder
import android.view.SurfaceView

class GameView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

    private val surfaceHolder: SurfaceHolder = holder
    private var gameLoopThread: GameLoopThread? = null
    private val backgroundColor = Paint().apply { color = android.graphics.Color.BLACK }

    init {
        surfaceHolder.addCallback(this)
        gameLoopThread = GameLoopThread(this)
        isFocusable = true
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        gameLoopThread?.isRunning = true
        gameLoopThread?.start()
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        // يمكنك إضافة منطق للتعامل مع تغيير حجم الشاشة هنا
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        var retry = true
        gameLoopThread?.isRunning = false
        while (retry) {
            try {
                gameLoopThread?.join()
                retry = false
            } catch (e: InterruptedException) {
                // حاول الانضمام مرة أخرى
            }
        }
    }

    override fun draw(canvas: Canvas) {
        super.draw(canvas)
        canvas.drawPaint(backgroundColor)
        // سيتم رسم عناصر اللعبة هنا
    }

    fun update() {
        // سيتم تحديث منطق اللعبة هنا
    }
}

class GameLoopThread(private val gameView: GameView) : Thread() {
    var isRunning: Boolean = false

    override fun run() {
        var lastTime = System.nanoTime()
        val targetFPS = 60
        val optimalTime = 1000000000.0 / targetFPS

        while (isRunning) {
            val now = System.nanoTime()
            val updateLength = now - lastTime
            lastTime = now
            val delta = updateLength / optimalTime

            gameView.update()
            val canvas: Canvas? = try {
                gameView.surfaceHolder.lockCanvas()
            } catch (e: Exception) {
                null
            }
            canvas?.let {
                synchronized(gameView.surfaceHolder) {
                    gameView.draw(it)
                }
                try {
                    gameView.surfaceHolder.unlockCanvasAndPost(it)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }

            val sleepTime = (optimalTime - (System.nanoTime() - now)).toLong() / 1000000
            if (sleepTime > 0) {
                try {
                    sleep(sleepTime)
                } catch (e: InterruptedException) {
                    // لا تفعل شيئًا
                }
            }
        }
    }
}


** شرح الكود **
- GameView: الكلاس الرئيسي لعرض اللعبة.
- SurfaceHolder.Callback: واجهة لتلقي إشعارات حول تغييرات سطح الرسم.
- GameLoopThread: كلاس منفصل يدير حلقة اللعبة (التحديث والرسم المستمر).
- surfaceCreated: يتم استدعاؤه عند إنشاء سطح الرسم. نبدأ هنا تشغيل حلقة اللعبة.
- surfaceChanged: يتم استدعاؤه عند تغيير حجم سطح الرسم.
- surfaceDestroyed: يتم استدعاؤه عند تدمير سطح الرسم. نوقف هنا حلقة اللعبة.
- draw(canvas: Canvas): يتم استدعاؤه لرسم عناصر اللعبة على الشاشة.
- update(): يتم استدعاؤه لتحديث منطق اللعبة (حركة اللاعب، حركة الأعداء، إلخ).
- GameLoopThread: ينفذ حلقة اللعبة القياسية (حساب الفارق الزمني، التحديث، الرسم، النوم).

3. إضافة عناصر اللعبة (اللاعب، الأعداء، الرصاص):

قم بإنشاء كلاسات Kotlin لتمثيل عناصر اللعبة المختلفة (مثل Player, Enemy, Bullet).
ستحتوي هذه الكلاسات على خصائص مثل الموقع، السرعة، الرسومات (Sprites)، ومنطق الحركة الخاص بها :

        


import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint

data class Vector2(var x: Float = 0f, var y: Float = 0f)

open class GameObject(var position: Vector2, var speed: Float = 0f, var sprite: Bitmap? = null) {
    open fun draw(canvas: Canvas, paint: Paint) {
        sprite?.let {
            canvas.drawBitmap(it, position.x - it.width / 2, position.y - it.height / 2, paint)
        }
    }

    open fun update() {
        // منطق التحديث العام للكائنات
    }
}

class Player(position: Vector2, speed: Float, sprite: Bitmap?) : GameObject(position, speed, sprite) {
    override fun update() {
        // منطق حركة اللاعب
    }

    fun shoot(): Bullet {
        // إنشاء رصاصة جديدة من موقع اللاعب
        return Bullet(Vector2(position.x, position.y), 20f, null) // تحتاج إلى إضافة Sprite للرصاصة
    }
}

class Enemy(position: Vector2, speed: Float, sprite: Bitmap?) : GameObject(position, speed, sprite) {
    override fun update() {
        // منطق حركة العدو
    }
}

class Bullet(position: Vector2, speed: Float, sprite: Bitmap?) : GameObject(position, speed, sprite) {
    override fun update() {
        position.y -= speed // حركة الرصاصة للأعلى
    }
}



** شرح الكود **
- Vector2: كلاس بسيط لتمثيل متجه ثنائي الأبعاد (للموقع والسرعة).
- GameObject: كلاس أساسي لجميع عناصر اللعبة يحتوي على الموقع والسرعة 
والـ Sprite ووظائف الرسم والتحديث الأساسية.
- Player: يرث من GameObject ويحتوي على منطق حركة اللاعب ووظيفة shoot() لإنشاء رصاصات.
- Enemy: يرث من GameObject ويحتوي على منطق حركة العدو.
- Bullet: يرث من GameObject ويحتوي على منطق حركة الرصاصة.
- draw(): يرسم الـ Sprite الخاص بالكائن على الـ Canvas.
- update(): يقوم بتحديث حالة الكائن في كل إطار من اللعبة.




4. تحميل الرسومات (Sprites) :

قم بإضافة ملفات الصور (PNG أو JPEG) لرسومات عناصر اللعبة إلى مجلد res/drawable في 
مشروع Android الخاص بك.
استخدم كلاس BitmapFactory لتحميل هذه الرسومات إلى كائنات Bitmap :

        


import android.content.Context
import android.graphics.BitmapFactory

fun loadBitmap(context: Context, resourceId: Int): Bitmap? {
    return BitmapFactory.decodeResource(context.resources, resourceId)
}

// في كلاس GameView أو مكان تهيئة اللعبة:
val playerSprite = loadBitmap(context, R.drawable.player_sprite)
val enemySprite = loadBitmap(context, R.drawable.enemy_sprite)
val bulletSprite = loadBitmap(context, R.drawable.bullet_sprite)

// عند إنشاء كائنات اللاعب والعدو والرصاص:
val player = Player(Vector2(width / 2f, height - 100f), 10f, playerSprite)
val enemies = mutableListOf<Enemy>()
val bullets = mutableListOf<Bullet>()





** شرح الكود **
- loadBitmap(): وظيفة مساعدة لتحميل صورة Bitmap من موارد التطبيق.
- يتم تحميل الـ Sprites في مكان تهيئة اللعبة (مثل داخل GameView أو في كلاس مدير اللعبة).
- يتم تمرير الـ Sprites المحملة إلى مُنشئات كائنات اللعبة.

5. إضافة منطق اللعبة الأساسي:

في وظيفة update() في GameView:
قم بتحديث موقع اللاعب بناءً على مدخلات المستخدم (اللمس، الإيماءات).
قم بتحديث حركة الأعداء (على سبيل المثال، التحرك بشكل عشوائي أو باتجاه اللاعب).
قم بتحديث حركة الرصاصات.
قم بإنشاء أعداء جدد بشكل دوري.
قم بإزالة الرصاصات التي خرجت من الشاشة :

        


// في كلاس GameView:
private val player = Player(Vector2(width / 2f, height - 100f), 10f, playerSprite)
private val enemies = mutableListOf<Enemy>()
private val bullets = mutableListOf<Bullet>()
private var lastEnemySpawnTime = System.currentTimeMillis()
private val enemySpawnInterval = 1000L // ميلي ثانية

override fun update() {
    // تحديث حركة اللاعب (تحتاج إلى إضافة معالجة الإدخال)
    // player.position.x += player.speed * deltaTime

    // تحديث حركة الأعداء
    enemies.forEach { it.update() }

    // تحديث حركة الرصاصات
    bullets.forEach { it.update() }
    bullets.removeAll { it.position.y < 0 } // إزالة الرصاصات خارج الشاشة

    // إنشاء أعداء جدد بشكل دوري
    val currentTime = System.currentTimeMillis()
    if (currentTime - lastEnemySpawnTime > enemySpawnInterval) {
        val randomX = (0 until width).random().toFloat()
        enemies.add(Enemy(Vector2(randomX, 50f), 5f, enemySprite))
        lastEnemySpawnTime = currentTime
    }
}

override fun draw(canvas: Canvas) {
    super.draw(canvas)
    canvas.drawPaint(backgroundColor)
    player.draw(canvas, Paint())
    enemies.forEach { it.draw(canvas, Paint()) }
    bullets.forEach { it.draw(canvas, Paint()) }
}

fun shoot() {
    bullets.add(player.shoot())
}


** شرح الكود **
- يتم تحديث موقع اللاعب، وحركة الأعداء، وحركة الرصاصات في كل إطار.
- يتم إنشاء أعداء جدد بشكل دوري باستخدام مؤقت.
- يتم رسم جميع عناصر اللعبة على الـ Canvas في وظيفة draw().
- وظيفة shoot() تسمح للاعب بإطلاق رصاصات.




6. اكتشاف التصادم :

قم بتنفيذ منطق لاكتشاف التصادم بين عناصر اللعبة (مثل رصاصات اللاعب والأعداء، اللاعب والأعداء).
عند حدوث تصادم، قم بتنفيذ الإجراءات المناسبة (مثل تدمير العدو، إنقاص حياة اللاعب) :

        


// في كلاس GameView:
private var score = 0
private val scorePaint = Paint().apply {
    color = android.graphics.Color.WHITE
    textSize = 50f
}

override fun update() {
    // ... (تحديث حركة اللاعب والأعداء والرصاص) ...

    // اكتشاف التصادم بين الرصاصات والأعداء
    val enemiesToRemove = mutableListOf<Enemy>()
    val bulletsToRemove = mutableListOf<Bullet>()
    for (enemy in enemies) {
        for (bullet in bullets) {
            if (checkCollision(enemy, bullet)) {
                enemiesToRemove.add(enemy)
                bulletsToRemove.add(bullet)
                score += 10 // زيادة النقاط عند تدمير عدو
            }
        }
    }
    enemies.removeAll(enemiesToRemove)
    bullets.removeAll(bulletsToRemove)

    // اكتشاف التصادم بين اللاعب والأعداء (تحتاج إلى إضافة منطق إنهاء اللعبة/إنقاص الحياة)
    for (enemy in enemies) {
        if (checkCollision(player, enemy)) {
            // منطق إنهاء اللعبة أو إنقاص حياة اللاعب
            // gameEnded = true
        }
    }
}

override fun draw(canvas: Canvas) {
    super.draw(canvas)
    // ... (رسم اللاعب والأعداء والرصاص) ...
    canvas.drawText("Score: $score", 50f, 100f, scorePaint)
}

fun checkCollision(obj1: GameObject, obj2: GameObject): Boolean {
    obj1.sprite?.let { sprite1 ->
        obj2.sprite?.let { sprite2 ->
            val rect1 = android.graphics.RectF(
                obj1.position.x - sprite1.width / 2,
                obj1.position.y - sprite1.height / 2,
                obj1.position.x + sprite1.width / 2,
                obj1.position.y + sprite1.height / 2
            )
            val rect2 = android.graphics.RectF(
                obj2.position.x - sprite2.width / 2,
                obj2.position.y - sprite2.height / 2,
                obj2.position.x + sprite2.width / 2,
                obj2.position.y + sprite2.height / 2
            )
            return RectF.intersects(rect1, rect2)
        }
    }
    return false
}


** شرح الكود **
- checkCollision(): وظيفة تتحقق مما إذا كان هناك تداخل بين مستطيلات حدود كائنين.
- يتم التحقق من التصادم بين الرصاصات والأعداء واللاعب والأعداء في وظيفة update().
- عند حدوث تصادم بين رصاصة وعدو، يتم تدميرهما وزيادة النقاط.
- يتم رسم النقاط على الشاشة في وظيفة draw().

7. معالجة مدخلات المستخدم:

قم بإضافة معالجة اللمس أو الإيماءات إلى GameView للتحكم في حركة اللاعب وإطلاق النار :

        


import android.view.MotionEvent
import android.view.View

// في كلاس GameView:
init {
    // ... (تهيئة أخرى) ...
    setOnTouchListener(object : OnTouchListener {
        override fun onTouch(v: View?, event: MotionEvent?): Boolean {
            event?.let {
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> {
                        // عند لمس الشاشة، قم بإطلاق النار
                        shoot()
                        return true
                    }
                    MotionEvent.ACTION_MOVE -> {
                        // تحديث موقع اللاعب بناءً على حركة اللمس
                        player.position.x = it.x
                        return true
                    }
                    MotionEvent.ACTION_UP -> {
                        // عند رفع الإصبع
                        return true
                    }
                }
            }
            return false
        }
    })
}


** شرح الكود **
- يتم استخدام setOnTouchListener للاستماع إلى أحداث اللمس على الشاشة.
- عند لمس الشاشة (ACTION_DOWN), يتم استدعاء وظيفة shoot().
- عند تحريك الإصبع (ACTION_MOVE), يتم تحديث موقع اللاعب على المحور X.


8. حفظ النقاط :

استخدم SharedPreferences في Android لحفظ أعلى نتيجة للعبة بشكل دائم :


        


import android.content.Context

// في كلاس GameView أو مدير اللعبة:
private val PREFS_NAME = "MyGamePrefs"
private val KEY_HIGH_SCORE = "highScore"
private val sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

fun saveHighScore() {
    val editor = sharedPreferences.edit()
    editor.putInt(KEY_HIGH_SCORE, getHighScore())
    editor.apply()
}

fun getHighScore(): Int {
    return sharedPreferences.getInt(KEY_HIGH_SCORE, 0)
}

// عند انتهاء اللعبة أو في أي وقت تريد حفظ النتيجة:
if (score > getHighScore()) {
    saveHighScore()
}

// لعرض أعلى نتيجة:
// canvas.drawText("High Score: ${getHighScore()}", ...)



** شرح الكود **
- SharedPreferences: واجهة لتخزين واسترداد بيانات بسيطة (قيم أساسية) بشكل دائم.
- يتم تعريف اسم ملف التفضيلات (PREFS_NAME) ومفتاح النتيجة العالية (KEY_HIGH_SCORE).
- saveHighScore(): يحفظ القيمة الحالية للمتغير score كأعلى نتيجة.
- getHighScore(): يسترد أعلى نتيجة محفوظة، أو 0 إذا لم يتم حفظ أي نتيجة بعد.
- يتم استدعاء saveHighScore() إذا كانت النتيجة الحالية أعلى من أعلى نتيجة محفوظة.
- يمكن عرض أعلى نتيجة على الشاشة باستخدام getHighScore().

9. نشر التطبيق :

1* إنشاء ملف APK/AAB :
- في Android Studio، انتقل إلى Build -> Build Bundle(s) / APK(s) -> Build APK(s) أو Build Bundle(s).
- سيتم إنشاء ملف APK (تطبيق Android Package) أو AAB (Android App Bundle) في مجلد app/build/outputs/apk/debug/ أو app/build/outputs/bundle/debug/.
2* توقيع التطبيق :
- قبل نشر التطبيق على متجر Google Play، يجب توقيعه باستخدام مفتاح توقيع رقمي. 
يمكنك إنشاء مفتاح جديد أو استخدام مفتاح موجود.
-  في Android Studio، انتقل إلى Build -> Generate Signed Bundle / APK.
3* إنشاء حساب مطور Google Play :
إذا لم يكن لديك حساب مطور Google Play، فستحتاج إلى إنشائه ودفع رسوم التسجيل.
4* إنشاء تطبيق جديد في Google Play Console :
قم بتسجيل الدخول إلى Google Play Console وانشئ تطبيقًا جديدًا.
5* تحميل ملف APK/AAB :
اتبع الإرشادات في Google Play Console لتحميل ملف APK أو AAB الخاص بتطبيقك.
6* إضافة التفاصيل والرسومات :
قم بإضافة اسم التطبيق، والوصف، والأيقونات، ولقطات الشاشة، ومقاطع الفيديو الترويجية.
7* تحديد التسعير والتوزيع :
حدد ما إذا كان تطبيقك مجانيًا أم مدفوعًا، واختر البلدان التي تريد توزيعه فيها.
8* مراجعة ونشر التطبيق :
راجع جميع التفاصيل وتأكد من امتثال تطبيقك لسياسات Google Play.
انقر فوق "نشر التطبيق". قد تستغرق عملية المراجعة بعض الوقت قبل أن يصبح تطبيقك متاحًا على المتجر.

* الخلاصة : 

لقد استعرضنا الخطوات الأساسية لإنشاء لعبة إطلاق نار ثنائية الأبعاد
 بسيطة باستخدام Kotlin على نظام Android. بدءًا من إعداد واجهة المستخدم
 وحلقة اللعبة، مرورًا بإضافة عناصر اللعبة ومنطقها الأساسي واكتشاف التصادم 
ومعالجة مدخلات المستخدم، وصولًا إلى حفظ النقاط ونشر التطبيق. هذا المقال يوفر
 أساسًا قويًا يمكنك البناء عليه لإضافة المزيد من الميزات والتحسينات لجعل لعبتك
 أكثر جاذبية وتشويقًا. تذكر أن تطوير الألعاب عملية تكرارية تتطلب الكثير من التجربة
 والتعلم المستمر. حظًا سعيدًا في رحلتك لتطوير الألعاب باستخدام Kotlin!


 
جدول المحتويات