
كيفية برمجة لعبة 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;
// ... داخل كلاس GameViewpublic 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;
// ... داخل كلاس GameViewprivate 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;}
@Overridepublic 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
@Overridepublic 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); // ...}
// في كلاس Playerpublic Player(Context context, int x, int y) { this.x = x; this.y = y; playerImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.player); // ...}
@Overridepublic void draw(Canvas canvas) { canvas.drawBitmap(playerImage, x, y, null);}
// في كلاس Platformpublic void draw(Canvas canvas) { canvas.drawBitmap(platformImage, x, y, null);}
// ... وهكذا لبقية العناصر
--
* ملاحظة: تأكد من إضافة ملفات الصور
(مثل player.png, platform.png, enemy.png, goal.png) إلى
مجلد res/drawable في مشروعك.
2. التعامل مع الاصطدامات بشكل كامل
في دالة update() في GameView:
Java
@Overridepublic 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 { // لا يوجد المزيد من المستويات - يمكنك عرض شاشة فوز }}
@Overridepublic 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); // استعادة الصعوبة المحفوظة // ...}
@Overridepublic 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;
// داخل كلاس GameViewprivate 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); } // ...}
@Overridepublic 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;}
@Overrideprotected 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@Overridepublic 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) { // ارسم رسالة نهاية اللعبة والنقاط وزر إعادة المحاولة}
@Overridepublic 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;}
@Overridepublic void surfaceCreated(SurfaceHolder holder) { if (thread == null || !thread.isAlive()) { thread = new MainThread(getHolder(), this); thread.setRunning(true); thread.start(); }}
@Overridepublic 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. يمكنك الآن توسيع
هذه اللعبة بإضافة المزيد من المستويات، والأعداء، والعناصر، والمؤثرات الصوتية،
والرسومات المتقدمة لجعلها أكثر جاذبية وتحديًا. تذكر أن تطوير الألعاب يتطلب صبرًا وممارسة،
ولا تتردد في البحث عن المزيد من الموارد والأمثلة عبر الإنترنت لتعزيز مهاراتك. حظًا سعيدًا في تطوير لعبتك!