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

الصفحات

كيفية برمجة لعبة Run and Jump ثنائية الأبعاد على Android Studio | Java

حالة اللعبة Android، شاشة البدء Android، الإيقاف المؤقت Android، صوت Android Game، MediaPlayer Android، SoundPool Android، صعوبة اللعبة Android، تعديل سلوك اللعبة، تحميل مستوى من ملف Android، تنسيق بيانات المستوى، الوصول إلى الهدف Android، تغيير المستوى Android، اكتشاف الاصطدامات Android، منع المرور، خسارة اللعبة، تحميل الصور Android، BitmapFactory Android، أصول اللعبة Android، شاشة نهاية اللعبة Android، تخطيط XML Android، Button Android، Intent Android، عناصر اللعبة Android، الأعداء Android، المنصات Android، الهدف Android، الاصطدامات Android، إدارة مستويات اللعبة Android، تحميل مستوى جديد، تغيير المستوى، حفظ نقاط اللعبة Android، SharedPreferences Android، استعادة نقاط اللعبة Android، حركة اللاعب Android، القفز Android، معالجة اللمس Android، تحديث موقع اللاعب، الجاذبية Android، تصميم مستويات اللعبة Android، مصفوفة ثنائية الأبعاد Java، تحميل مستويات اللعبة من ملف، تمثيل عناصر المستوى، تخطيط XML Android، إعدادات اللعبة Android، Button Android، CheckBox Android، SeekBar Android، Intent Android، إنشاء مشروع Android Studio، واجهة مستخدم Android، GameView Android، تطوير ألعاب Android Java، برمجة ألعاب ركض واقفز Android، لعبة قفز Android، لعبة منصات Android، Canvas Android Game، Run and Jump، على اندرويد ستوديو، تطوير ألعاب Android Java، برمجة ألعاب ركض واقفز Android، لعبة قفز Android، لعبة منصات Android، Canvas Android Game، معالجة اللمس Android Game، حلقة اللعبة Android، مستويات اللعبة Android، حفظ نقاط اللعبة Android، إعدادات اللعبة Android، إنشاء مشروع Android Studio، تخطيط XML Android، واجهة مستخدم Android، GameView Android. برمجة لعبة ركض واقفز (Run and Jump) على اندرويد ستوديو، برمجة لعبة ركض واقفز ثنائية الأبعاد، Android Studio، Java، برمجة لعبة ركض و اقفز ثنائية الأبعاد متكاملة على Android Studio باستخدام Java، How to code a 2D run and jump game in Android Studio | Java، كيفية برمجة لعبة Run and Jump ثنائية الأبعاد على Android Studio | Java، SharedPreferences Android، إنشاء مشروع Android Studio، SeekBar،
 



كيفية برمجة لعبة Run and Jump ثنائية الأبعاد على Android Studio | Java


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


خطوات برمجة لعبة Run and Jump ثنائية الأبعاد على Android Studio


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

الخطوة الأولى: إنشاء مشروع Android جديد وإعداد الواجهة الرئيسية

- إنشاء مشروع Android جديد: ابدأ مشروعًا جديدًا في Android Studio واختر
 قالب "Empty Activity"، سمِّ مشروعك (مثل RunAndJumpGame) واختر Java كلغة البرمجة.
- إنشاء كلاس GameView: قم بإنشاء كلاس Java جديد باسم GameView يمتد من SurfaceView وينفذ SurfaceHolder.Callback. هذا الكلاس سيكون مسؤولاً عن رسم عناصر اللعبة وتحديثها.
Java




package your_package_name;

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

public class GameView extends SurfaceView implements SurfaceHolder.Callback {

    private MainThread thread;

    public GameView(Context context) {
        super(context);
        getHolder().addCallback(this);
        thread = new MainThread(getHolder(), this);
        setFocusable(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        while (retry) {
            try {
                thread.setRunning(false);
                thread.join();
            } catch (InterruptedException e) {
                // حاول الإيقاف مرة أخرى
            }
            retry = false;
        }
    }

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

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        // رسم عناصر اللعبة هنا
    }
}


--

* إنشاء كلاس MainThread (حلقة اللعبة): 
قم بإنشاء كلاس Java جديد باسم MainThread لإدارة حلقة اللعبة.

Java




package your_package_name;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

    private SurfaceHolder surfaceHolder;
    private GameView gameView;
    private boolean running;
    public static Canvas canvas;

    public MainThread(SurfaceHolder surfaceHolder, GameView gameView) {
        super();
        this.surfaceHolder = surfaceHolder;
        this.gameView = gameView;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        long startTime;
        long timeMillis;
        long waitTime;
        long targetFPS = 30;
        long averageFPS = 1000 / targetFPS;

        while (running) {
            startTime = System.nanoTime();
            canvas = null;
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    this.gameView.update();
                    this.gameView.draw(canvas);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (canvas != null) {
                    try {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            timeMillis = (System.nanoTime() - startTime) / 1000000;
            waitTime = averageFPS - timeMillis;
            try {
                if (waitTime > 0) {
                    this.sleep(waitTime);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


--

* تعديل activity_main.xml: استبدل الـ TextView الافتراضي بـ GameView.
XML

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <your_package_name.GameView
        android:id="@+id/gameView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
--

الخطوة الثانية: تصميم لوحة الإعدادات


* إنشاء تخطيط activity_settings.xml: قم بإنشاء ملف تخطيط 
XML جديد باسم activity_settings.xml لتصميم واجهة الإعدادات (مثل خيارات الصوت، صعوبة اللعبة).
XML




<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="الإعدادات"
        android:textSize="24sp"
        android:textStyle="bold"
        android:layout_marginBottom="16dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="الصوت:"
            android:textSize="18sp" />

        <CheckBox
            android:id="@+id/soundCheckBox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginTop="16dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="صعوبة اللعبة:"
            android:textSize="18sp" />

        <SeekBar
            android:id="@+id/difficultySeekBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:min="1"
            android:max="3"
            android:progress="1"
            android:layout_marginStart="8dp" />

        <TextView
            android:id="@+id/difficultyTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="سهل"
            android:textSize="18sp"
            android:layout_marginStart="8dp" />

    </LinearLayout>

    <Button
        android:id="@+id/backButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="رجوع"
        android:layout_marginTop="32dp" />

</LinearLayout>


--

* إنشاء SettingsActivity.java: قم بإنشاء كلاس Java جديد باسم
 SettingsActivity للتعامل مع منطق الإعدادات.
Java




package your_package_name;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class SettingsActivity extends AppCompatActivity {

    private CheckBox soundCheckBox;
    private SeekBar difficultySeekBar;
    private TextView difficultyTextView;
    private Button backButton;
    private SharedPreferences sharedPreferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);

        soundCheckBox = findViewById(R.id.soundCheckBox);
        difficultySeekBar = findViewById(R.id.difficultySeekBar);
        difficultyTextView = findViewById(R.id.difficultyTextView);
        backButton = findViewById(R.id.backButton);
        sharedPreferences = getSharedPreferences("game_settings", MODE_PRIVATE);

        // استعادة الإعدادات المحفوظة
        soundCheckBox.setChecked(sharedPreferences.getBoolean("sound_enabled", true));
        difficultySeekBar.setProgress(sharedPreferences.getInt("difficulty", 1));
        updateDifficultyText(difficultySeekBar.getProgress());

        difficultySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                updateDifficultyText(progress);
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putInt("difficulty", progress);
                editor.apply();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });

        soundCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putBoolean("sound_enabled", isChecked);
            editor.apply();
        });

        backButton.setOnClickListener(v -> {
            Intent intent = new Intent(SettingsActivity.this, MainActivity.class);
            startActivity(intent);
            finish();
        });
    }

    private void updateDifficultyText(int difficulty) {
        switch (difficulty) {
            case 1:
                difficultyTextView.setText("سهل");
                break;
            case 2:
                difficultyTextView.setText("متوسط");
                break;
            case 3:
                difficultyTextView.setText("صعب");
                break;
        }
    }
}


--

* إضافة زر الإعدادات في MainActivity.java: أضف زرًا في تخطيط 
activity_main.xml (داخل FrameLayout) وقم بتعيين مستمع له لفتح SettingsActivity.

    <Button
android:id="@+id/settingsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="الإعدادات"
android:layout_gravity="top|end"
android:layout_margin="16dp" />
-- 

java

// داخل onCreate في MainActivity
Button settingsButton = findViewById(R.id.settingsButton);
settingsButton.setOnClickListener(v -> {
    Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
    startActivity(intent);
});
--

الخطوة الثالثة: تصميم مستويات اللعبة

- تمثيل المستوى: يمكنك تمثيل مستوى اللعبة باستخدام مصفوفة ثنائية الأبعاد 
حيث يمثل كل عنصر نوعًا من الكائنات (مثل اللاعب، منصة، عدو، هدف).
Java

private int[][] levelData = {
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {0, 0, 0, 0, 2, 0, 0, 0, 0, 0},
        {1, 0, 1, 0, 1, 0, 1, 1, 1, 1},
        {0, 2, 0, 0, 0, 0, 0, 0, 0, 3}, // 3 يمثل الهدف
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

private int tileSize;
--

- تحميل المستوى (اختياري): يمكنك تحميل بيانات المستوى
 من ملف نصي أو JSON لتسهيل إنشاء مستويات متعددة.
- رسم المستوى في GameView.draw(): قم بتكرار مصفوفة levelData 
ورسم الكائنات المقابلة على الـ Canvas بناءً على قيم المصفوفة. 
ستحتاج إلى كلاسات منفصلة لتمثيل اللاعب، المنصات، الأعداء، والهدف.




الخطوة الرابعة: تنفيذ حركة اللاعب والقفز

- كلاس Player: قم بإنشاء كلاس Java باسم Player لتمثيل اللاعب.
Java




package your_package_name;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;

public class Player {
    private Bitmap image;
    private int x, y;
    private int speedX;
    private int speedY;
    private boolean isJumping;
    private float gravity = 1.5f;
    private float jumpForce = -30;

    public Player(Bitmap image, int x, int y) {
        this.image = image;
        this.x = x;
        this.y = y;
        speedX = 10;
        isJumping = false;
    }

    public Rect getCollisionRect() {
        return new Rect(x, y, x + image.getWidth(), y + image.getHeight());
    }

    public void update() {
        x += speedX;
        if (isJumping) {
            speedY += gravity;
            y += speedY;
            if (y >= initialY) { // وصل إلى الأرض
                y = initialY;
                speedY = 0;
                isJumping = false;
            }
        }
    }

    private int initialY;

    public void startJump() {
        if (!isJumping) {
            isJumping = true;
            speedY = jumpForce;
            initialY = y;
        }
    }

    public void draw(Canvas canvas) {
        canvas.drawBitmap(image, x, y, null);
    }

    // Getter and Setter methods
}


--

- معالجة اللمس في GameView: قم بتنفيذ OnTouchListener في 
GameView للتعامل مع لمسات الشاشة لتحريك اللاعب والقفز.
Java




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

// ... داخل كلاس GameView
public class GameView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {

    private Player player;
    // ...

    public GameView(Context context) {
        // ...
        setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                player.startJump();
                break;
            case MotionEvent.ACTION_MOVE:
                if (event.getX() > getWidth() / 2) {
                    player.setSpeedX(10); // تحريك لليمين
                } else {
                    player.setSpeedX(-10); // تحريك لليسار
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                player.setSpeedX(0); // توقف
                    break;
        }
        return true;
    }

    @Override
    public void update() {
        player.update();
        // تحديث مواقع الأعداء، التحقق من الاصطدامات، إلخ.
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        player.draw(canvas);
        // رسم المنصات، الأعداء، الهدف، إلخ.
    }
}


--

الخطوة الخامسة: تنفيذ حفظ النقاط

متغير النقاط: أضف متغيرًا لتتبع نقاط اللاعب في كلاس GameView.
private int score = 0;
--

- زيادة النقاط: قم بزيادة قيمة score عند جمع عنصر معين أو الوصول إلى نقطة تفتيش.
- حفظ النقاط في onPause() في MainActivity: استخدم 
SharedPreferences لحفظ قيمة score عند إيقاف اللعبة مؤقتًا.
Java

// داخل MainActivity.java
@Override
protected void onPause() {
    super.onPause();
    SharedPreferences sharedPreferences = getSharedPreferences("game_data", MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putInt("score", gameView.getScore()); // افترض وجود دالة getScore() في GameView
    editor.apply();
}
--

* استعادة النقاط في onCreate() في MainActivity: استرجع قيمة 
score المحفوظة عند إنشاء الـ MainActivity.
Java

// داخل MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    gameView = findViewById(R.id.gameView);
    SharedPreferences sharedPreferences = getSharedPreferences("game_data", MODE_PRIVATE);
    int savedScore = sharedPreferences.getInt("score", 0); // 0 هي القيمة الافتراضية
    gameView.setScore(savedScore); // افترض وجود دالة setScore() في GameView
    // ...
}
--

- عرض النقاط: ارسم قيمة score على الـ Canvas في دالة GameView.draw().
Java




import android.graphics.Color;
import android.graphics.Paint;

// ... داخل كلاس GameView
private Paint scorePaint;

public GameView(Context context) {
    // ...
    scorePaint = new Paint();
    scorePaint.setColor(Color.BLACK);
    scorePaint.setTextSize(40);
}

public int getScore() {
    return score;
}

public void setScore(int score) {
    this.score = score;
}

@Override
public void draw(Canvas canvas) {
    super.draw(canvas);
    player.draw(canvas);
    // رسم المنصات، الأعداء، الهدف، إلخ.
    canvas.drawText("النقاط: " + score, 50, 50, scorePaint);
}


--

الخطوة السادسة: إدارة مستويات اللعبة

- متغير المستوى الحالي: أضف متغيرًا لتتبع المستوى الحالي في كلاس GameView.
private int currentLevel = 1;
--

- تحميل مستوى جديد: قم بإنشاء دالة لتحميل بيانات مستوى جديد بناءً على قيمة currentLevel.
- التحقق من الوصول إلى الهدف: في دالة update()‎، تحقق مما إذا كان
 اللاعب قد وصل إلى الهدف. إذا كان الأمر كذلك، فقم بزيادة currentLevel 
واستدعاء دالة تحميل المستوى الجديد.
Java

// داخل كلاس GameView
private void loadLevel(int level) {
    // قم بتحميل بيانات المستوى من levelData أو من ملف
    // وقم بتهيئة مواقع اللاعب، المنصات، الأعداء، والهدف
    levelData = // بيانات المستوى الجديد
    // ... تهيئة عناصر المستوى
}

@Override
public void update() {
    player.update();
    // تحديث مواقع الأعداء، التحقق من الاصطدامات
    if (playerReachedGoal()) {
        currentLevel++;
        loadLevel(currentLevel);
    }
}
--

الخطوة السابعة: إضافة عناصر اللعبة الأخرى

- كلاسات للعناصر: قم بإنشاء كلاسات Java لتمثيل العناصر الأخرى في
 اللعبة مثل Platform، Enemy، و Goal. يجب أن تحتوي هذه الكلاسات على
 خصائص مثل الموقع، الأبعاد، والصور (إذا لزم الأمر)، ودالة draw(Canvas canvas) لرسمها.
- قوائم للعناصر: في كلاس GameView، قم بإنشاء قوائم لتخزين مثيلات هذه الكلاسات.
Java

private List<Platform> platforms;
private List<Enemy> enemies;
private Goal goal;
--

- تهيئة العناصر في loadLevel(): عند تحميل مستوى جديد، قم بإنشاء 
مثيلات للعناصر وإضافتها إلى القوائم بناءً على بيانات المستوى.
- رسم العناصر في draw(): قم بتكرار القوائم ورسم كل عنصر على الـ Canvas.
- تنفيذ اكتشاف الاصطدامات في update(): قم بالتحقق من الاصطدامات بين اللاعب
 وكل عنصر آخر (المنصات، الأعداء، الهدف). تعامل مع الاصطدامات بشكل مناسب
 (مثل منع اللاعب من المرور عبر المنصات، خسارة اللعبة عند الاصطدام بعدو،
 زيادة النقاط والتحميل إلى المستوى التالي عند الوصول إلى الهدف).
Java




@Override
public void update() {
    player.update();
    for (Platform platform : platforms) {
        if (player.getCollisionRect().intersect(platform.getRect())) {
            // التعامل مع الاصطدام بالمنصة (منع السقوط)
        }
    }
    for (Enemy enemy : enemies) {
        if (player.getCollisionRect().intersect(enemy.getRect())) {
            // التعامل مع الاصطدام بالعدو (خسارة اللعبة)
        }
    }
    if (player.getCollisionRect().intersect(goal.getRect())) {
        score += 100;
        currentLevel++;
        loadLevel(currentLevel);
    }
}


--

الخطوة الثامنة: إضافة شاشة نهاية اللعبة

- إنشاء تخطيط activity_game_over.xml: قم بإنشاء ملف تخطيط XML 
جديد لعرض شاشة نهاية اللعبة (مثل عرض النقاط النهائية وزر لإعادة المحاولة).
- إنشاء GameOverActivity.java: قم بإنشاء كلاس Java جديد لعرض
 شاشة نهاية اللعبة والتعامل مع تفاعلات المستخدم.
- الانتقال إلى شاشة نهاية اللعبة: عند خسارة اللاعب (الاصطدام بعدو)، قم بإنشاء Intent
 للانتقال إلى GameOverActivity وإرسال النقاط النهائية معه.
- التعامل مع زر إعادة المحاولة: في GameOverActivity، قم بتعيين مستمع لزر
 إعادة المحاولة لبدء الـ MainActivity مرة أخرى.

خطوات مقترحة لتوسيع لعبة الركض والقفز Run and Jump على اندرويد ستوديو


1. تحميل الصور (Bitmap)

في كلاس GameView أو الكلاسات الخاصة بالعناصر (مثل Player، Platform، Enemy):
Java




import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

// داخل كلاس GameView أو Player أو غيره
private Bitmap playerImage;
private Bitmap platformImage;
private Bitmap enemyImage;
private Bitmap goalImage;

public GameView(Context context) {
    super(context);
    // ...
    loadBitmaps();
}

private void loadBitmaps() {
    playerImage = BitmapFactory.decodeResource(getResources(), R.drawable.player);
    platformImage = BitmapFactory.decodeResource(getResources(), R.drawable.platform);
    enemyImage = BitmapFactory.decodeResource(getResources(), R.drawable.enemy);
    goalImage = BitmapFactory.decodeResource(getResources(), R.drawable.goal);

    // يمكنك أيضًا تغيير حجم الصور إذا لزم الأمر
    // playerImage = Bitmap.createScaledBitmap(playerImage, newWidth, newHeight, false);
    // ...
}

// في كلاس Player
public Player(Context context, int x, int y) {
    this.x = x;
    this.y = y;
    playerImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.player);
    // ...
}

@Override
public void draw(Canvas canvas) {
    canvas.drawBitmap(playerImage, x, y, null);
}

// في كلاس Platform
public void draw(Canvas canvas) {
    canvas.drawBitmap(platformImage, x, y, null);
}

// ... وهكذا لبقية العناصر


--

* ملاحظة: تأكد من إضافة ملفات الصور 
(مثل player.png, platform.png, enemy.png, goal.png) إلى
 مجلد res/drawable في مشروعك.




2. التعامل مع الاصطدامات بشكل كامل

في دالة update() في GameView:
Java




@Override
public void update() {
    player.update();

    // الاصطدام بالمنصات
    for (Platform platform : platforms) {
        if (player.getCollisionRect().intersect(platform.getRect())) {
            // منع اللاعب من السقوط أو المرور من الأعلى
            if (player.getSpeedY() > 0 && player.getCollisionRect().bottom > platform.getRect().top && player.getCollisionRect().bottom < platform.getRect().bottom) {
                player.setY(platform.getRect().top - player.getImage().getHeight());
                player.setSpeedY(0);
                player.setJumping(false);
            } else if (player.getSpeedY() < 0 && player.getCollisionRect().top < platform.getRect().bottom && player.getCollisionRect().top > platform.getRect().top) {
                player.setY(platform.getRect().bottom);
                player.setSpeedY(0);
            }
        }
    }

    // الاصطدام بالأعداء
    for (Enemy enemy : enemies) {
        if (player.getCollisionRect().intersect(enemy.getRect())) {
            // خسارة اللعبة
            gameOver();
        }
    }

    // الوصول إلى الهدف
    if (goal != null && player.getCollisionRect().intersect(goal.getRect())) {
        score += 100;
        currentLevel++;
        loadLevel(currentLevel);
    }
}

private void gameOver() {
    thread.setRunning(false);
    // يمكنك هنا الانتقال إلى شاشة نهاية اللعبة أو عرض رسالة
    // Intent intent = new Intent(getContext(), GameOverActivity.class);
    // getContext().startActivity(intent);
}


--

3. منطق playerReachedGoal()

يمكن دمج هذا المنطق مباشرة في دالة update() كما في المثال أعلاه. إذا كنت تفضل دالة منفصلة:
Java

private boolean playerReachedGoal() {
    return goal != null && player.getCollisionRect().intersect(goal.getRect());
}

@Override
public void update() {
    player.update();
    // ... التحقق من الاصطدامات الأخرى
    if (playerReachedGoal()) {
        score += 100;
        currentLevel++;
        loadLevel(currentLevel);
    }
}
--

4. تحميل بيانات المستوى الفعلي


يمكنك تحميل المستوى من مصفوفة مباشرة (كما في المثال الأولي) أو 
من ملف نصي أو JSON. إليك مثال للتحميل من مصفوفة:
Java




private int[][] levelData;
private int tileSize;
private List<Platform> platforms;
private List<Enemy> enemies;
private Goal goal;

public GameView(Context context) {
    super(context);
    // ...
    tileSize = getWidth() / 10; // مثال لحساب حجم البلاطة
    platforms = new ArrayList<>();
    enemies = new ArrayList<>();
    loadLevel(currentLevel);
}

private void loadLevel(int level) {
    platforms.clear();
    enemies.clear();
    goal = null;

    // بيانات المستوى (يمكن نقلها إلى ملفات منفصلة)
    if (level == 1) {
        levelData = new int[][]{
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {1, 0, 1, 0, 2, 0, 0, 0, 0, 0},
                {1, 0, 1, 0, 1, 0, 1, 1, 1, 1},
                {0, 2, 0, 0, 0, 0, 0, 0, 0, 3},
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
        };
    } else if (level == 2) {
        levelData = new int[][]{
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                {0, 2, 0, 2, 0, 2, 0, 2, 0, 0},
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                {0, 0, 0, 0, 0, 0, 0, 0, 2, 3},
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
        };
    }
    // ... المزيد من المستويات

    if (levelData != null) {
        for (int row = 0; row < levelData.length; row++) {
            for (int col = 0; col < levelData[row].length; col++) {
                int tileType = levelData[row][col];
                int x = col * tileSize;
                int y = row * tileSize;

                switch (tileType) {
                    case 1:
                        platforms.add(new Platform(platformImage, x, y));
                        break;
                    case 2:
                        enemies.add(new Enemy(enemyImage, x, y));
                        break;
                    case 3:
                        goal = new Goal(goalImage, x, y);
                        break;
                    case 4:
                        player = new Player(getContext(), x, y); // تهيئة اللاعب عند تحميل المستوى
                        break;
                }
            }
        }
    } else {
        // لا يوجد المزيد من المستويات - يمكنك عرض شاشة فوز
    }
}

@Override
public void draw(Canvas canvas) {
    super.draw(canvas);
    player.draw(canvas);
    for (Platform platform : platforms) {
        platform.draw(canvas);
    }
    for (Enemy enemy : enemies) {
        enemy.draw(canvas);
    }
    if (goal != null) {
        goal.draw(canvas);
    }
    canvas.drawText("النقاط: " + score, 50, 50, scorePaint);
}


--

5. التعامل مع صعوبة اللعبة

في GameView:
Java




private int difficulty; // 1: سهل، 2: متوسط، 3: صعب

public GameView(Context context) {
    super(context);
    // ...
    SharedPreferences prefs = context.getSharedPreferences("game_settings", Context.MODE_PRIVATE);
    difficulty = prefs.getInt("difficulty", 1); // استعادة الصعوبة المحفوظة
    // ...
}

@Override
public void update() {
    // تعديل سلوك الأعداء بناءً على الصعوبة
    for (Enemy enemy : enemies) {
        if (difficulty == 1) {
            enemy.setSpeedX(5); // سرعة أبطأ
        } else if (difficulty == 2) {
            enemy.setSpeedX(10);
        } else if (difficulty == 3) {
            enemy.setSpeedX(15); // سرعة أعلى
        }
        enemy.update(); // تحديث حركة العدو
    }

    // تعديل قوة قفز اللاعب أو الجاذبية بناءً على الصعوبة (اختياري)
    if (difficulty == 3) {
        player.setGravity(2.0f); // جاذبية أقوى
    } else {
        player.setGravity(1.5f);
    }

    player.update();
    // ... بقية تحديثات اللعبة
}


--

* في كلاس Enemy:

Java

private int speedX;

public void setSpeedX(int speedX) {
    this.speedX = speedX;
}

public void update() {
    x += speedX;
    // ... منطق حركة العدو (مثل الانعكاس عند الحواف)
}
--

6. تشغيل الصوت

Java




import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;

// داخل كلاس GameView
private MediaPlayer backgroundMusic;
private SoundPool soundPool;
private int jumpSoundId;
private boolean soundEnabled;

public GameView(Context context) {
    super(context);
    // ...
    SharedPreferences prefs = context.getSharedPreferences("game_settings", Context.MODE_PRIVATE);
    soundEnabled = prefs.getBoolean("sound_enabled", true);

    if (soundEnabled) {
        backgroundMusic = MediaPlayer.create(context, R.raw.background_music);
        backgroundMusic.setLooping(true);
        backgroundMusic.start();

        soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        jumpSoundId = soundPool.load(context, R.raw.jump_sound, 1);
    }
    // ...
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            player.startJump();
            if (soundEnabled) {
                soundPool.play(jumpSoundId, 1.0f, 1.0f, 0, 0, 1.0f);
            }
            break;
        // ...
    }
    return true;
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (backgroundMusic != null) {
        backgroundMusic.release();
        backgroundMusic = null;
    }
    if (soundPool != null) {
        soundPool.release();
        soundPool = null;
    }
}


--

* ملاحظة: أضف ملفات الصوت 
(مثل background_music.mp3, jump_sound.wav) إلى مجلد res/raw.

7. إدارة حالة اللعبة بشكل أكثر تفصيلاً

* في كلاس GameView:

Java




private enum GameState {
    MENU, PLAYING, PAUSED, GAME_OVER
}

private GameState gameState = GameState.MENU;

// في دالة draw
@Override
public void draw(Canvas canvas) {
    super.draw(canvas);
    if (gameState == GameState.MENU) {
        drawMenu(canvas);
    } else if (gameState == GameState.PLAYING) {
        drawGame(canvas);
    } else if (gameState == GameState.PAUSED) {
        drawPaused(canvas);
    } else if (gameState == GameState.GAME_OVER) {
        drawGameOver(canvas);
    }
}

// دوال لرسم كل حالة
private void drawMenu(Canvas canvas) {
    // ارسم عناصر القائمة (زر ابدأ، زر الإعدادات)
}

private void drawGame(Canvas canvas) {
    player.draw(canvas);
    for (Platform platform : platforms) {
        platform.draw(canvas);
    }
    for (Enemy enemy : enemies) {
        enemy.draw(canvas);
    }
    if (goal != null) {
        goal.draw(canvas);
    }
    canvas.drawText("النقاط: " + score, 50, 50, scorePaint);
}

private void drawPaused(Canvas canvas) {
    drawGame(canvas); // ارسم اللعبة في الخلفية
    // ارسم عناصر شاشة الإيقاف المؤقت (زر استئناف، زر القائمة الرئيسية)
}

private void drawGameOver(Canvas canvas) {
    // ارسم رسالة نهاية اللعبة والنقاط وزر إعادة المحاولة
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    float x = event.getX();
    float y = event.getY();

    if (gameState == GameState.MENU) {
        // التعامل مع لمسات القائمة
        if (// تم لمس زر ابدأ) {
            gameState = GameState.PLAYING;
            loadLevel(currentLevel);
        } else if (// تم لمس زر الإعدادات) {
            Intent intent = new Intent(getContext(), SettingsActivity.class);
            getContext().startActivity(intent);
        }
    } else if (gameState == GameState.PLAYING) {
        // التعامل مع لمسات اللعب (القفز والتحريك)
        // ...
        if (// تم لمس زر الإيقاف المؤقت) {
            gameState = GameState.PAUSED;
            thread.setRunning(false);
        }
    } else if (gameState == GameState.PAUSED) {
        // التعامل مع لمسات شاشة الإيقاف المؤقت
        if (// تم لمس زر الاستئناف) {
            gameState = GameState.PLAYING;
            thread.setRunning(true);
        } else if (// تم لمس زر القائمة الرئيسية) {
            gameState = GameState.MENU;
        }
    } else if (gameState == GameState.GAME_OVER) {
        // التعامل مع لمسات شاشة نهاية اللعبة (إعادة المحاولة)
        if (// تم لمس زر إعادة المحاولة) {
            gameState = GameState.PLAYING;
            score = 0;
            currentLevel = 1;
            loadLevel(currentLevel);
            thread.setRunning(true);
            thread.start();
        }
    }
    return true;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    if (thread == null || !thread.isAlive()) {
        thread = new MainThread(getHolder(), this);
        thread.setRunning(true);
        thread.start();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
    while (retry) {
        try {
            thread.setRunning(false);
            thread.join();
        } catch (InterruptedException e) {
            // حاول الإيقاف مرة أخرى
        }
        retry = false;
    }
}


--

تذكر أنك ستحتاج إلى إنشاء الكلاسات الخاصة بالعناصر (Platform, Enemy, Goal)
 وتطبيق منطقها الداخلي. كما ستحتاج إلى تصميم وتضمين الأصول الرسومية والصوتية.

الخلاصة

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



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