




السمعة:
- إنضم22 يناير 2024
- المشاركات 227
- الحلول 3
- مستوى التفاعل 525
- النقاط 93
بسم الله الرحمن الرحيم
بعد أن قمنا بتعلم أساسيات الرسم يتبقى لنا التحريك (Animation)
- تحريك الكرة
ومن أجل تحريكها يجب تحديث سرعتها بالنسبة لكِلا المعلمين
C++:
#include <iostream>
#include<raylib.h>
// بما أن للكرة خصائص فسنحتاج لانشاء كلاس
class Ball {
public:
float x, y;
int speed_x, speed_y;
int radius;
void Draw() {//<----- نفس دالة الرسم التي استعملناها سابقا
DrawCircle(x,y, radius, WHITE);//xوy احداثيات الرسم للكرة
}
void Move() {//<--- اضفنا هذه الدالة من اجل تحديث سرعة الكرة
x += speed_x;//<-- Xسنقوم في كل مرة باضافة عدد معين من البكسلات بالنسبة للمحور
y+= speed_y;//<-- Yنفس الامر بالنسبة للمحور
}
};
Ball ball;//<--انشاء الكرة
int main()
{
const int screen_width = 1270;
const int screen_height = 550;
InitWindow(screen_width, screen_height, "Sh3ll Game");
SetTargetFPS(65);
ball.x = screen_width / 2;// اعطاء القيم المناسبة للكرة التي قمنا باختيارها في المرة السابقة
ball.y= screen_height / 2;
ball.radius = 20;
ball.speed_x = 5;
ball.speed_y = 5;
while (!WindowShouldClose()) {
BeginDrawing();
ball.Move();//نحدث
DrawLine(screen_width / 2, 0, screen_width / 2, screen_height, WHITE);
ball.Draw();//ثم نرسم
DrawRectangle(10, screen_height / 2- 60, 35, 120, WHITE);
DrawRectangle(screen_width-45, screen_height / 2 - 60, 35, 120, WHITE);
EndDrawing();
}
CloseWindow();
return 0;
}
النتيجة:
نلاحظ أن الكرة تتحرك لكن تترك خلفها أثر الموقع السابق, السبب في ذلك يعود إلى أن تنفيذ الحلقة يكون تنفيذ تسلسلي في كل مرة تقوم بتنفيذ الكود بشكل متتالي أمر بعد أمر بالترتيب المكتوب ولا تقوم بمحو ما سبقه مما قامت بتفيذه.
إذًا نحتاج في كل مرة قبل أن نبدأ برسم المواقع الجديدة وأن نمحو ما قمنا برسمه سابقًا
C++:
ClearBackground(BLACK);//<-- تاخذ هذه الدالة اللون الذي تقوم بالمحو به نختار الاسود
النتيجة:
قد قمت بتبطيئ السرعة كي استطيع أن القطها لكم لأنها سريعة.
ملاحظة: إن الكرة تتحرك إلى خارج إطار النافذة وبذلك يجب علينا تحديد تحركها !
الشرط سيكون عند ملامسة الكرة لإحدى حواف الشاشة يجب عليها تغيير مسارها
إذا ارتطمت الكرة بالشاشة يجب عليها تغيير مسارها( عكس اتجاهها)
C++:
void Move() {
x += speed_x;
y+= speed_y;
if (y + radius >= GetScreenHeight() || y-radius<=0) {//نتاكد من الارتطام اسفل واعلى الشاشة
speed_y *= -1;// تغير الاتجاه بعكس المسار
}
if (x + radius >= GetScreenWidth()|| x - radius <= 0) {//نفس الامر بالنسبة لجانبي الشاشة
speed_x *= -1;
}
}
};
النتيجة :
بالنسبة للمجاذيف:
- مجذاف اللاعب:
بما أنه هو كذلك له خصائص كالحركة فسنقومم أولًا بإنشاء كلاس له ولكن حركته متعلقة فقط بالمحور Y , إذًا سنهمل حركته بالنسبة للمحور X
كذلك , حركة اللاعب متعلقة بالضغط على القفلين UP أو DOWN إذًا سيكون هذا شرطنا .
C++:
class Paddle {
public:
float x, y,width, height;//<--- كنا قد حددناها سابقا XY بالنسبة لاحداثيات الرسم
int speed;
void Draw() {
DrawRectangle(x, y, width, height, WHITE);
}
void Move() {
if (IsKeyDown(KEY_UP)) {//<----- اذا ضغط الاعب على القفل ,↑ يجب ان نحرك المجذاف للاعلى بالتالي قيمته في المحور ستنقص
y -= speed;
}
if (IsKeyDown(KEY_DOWN)) { //اذا ضغط اللاعب على القفل ↓يجب ان نحركه نح الاسفل بالتالي ستزيد قيمته بالنسبة للمحور
y += speed;
}
}
};
[SIZE=5]لا ننسى ان نراقب الحواف اثناء التحريك[/SIZE]
[CODE=cpp]protected:
void Movement_limit() {
if (y <= 0) {//نراقب اذا ارتطمت بالحافة العلوية
y = 0;// نرجعها للبداية
}
if (y + height >= GetScreenHeight()) {//كذلك الامر بالنسبة للحافة السفلية
y = GetScreenHeight() - height;
}
}
النتيجة :
- تحريك مجذاف CPU:
بما أننا حددنا مسبقًا كلاس المجذاف؛ يمككننا فقط وراثة هذا الكلاس بالنسبة للمجذاف الخاص بال CPU؛ لكن مع إضافة الدالة التي تسمح بتحريك المجذاف بشكل أوتوماتيكي
كيف يجب أن يعمل هذا المجذاف أولًا :
ببساطة سنعتمد على هذه الخوارزمية
إذًا نحن في كل لحظة بحاجة لمعرفة قيمة y للكرة كي نستطيع تحريك المجذاف على أساسه .
C++:
class CPU_paddle :public Paddle{//<--- عرفنا الكلاس على انها ترث
public:
void Move(int ball_y) {//بماننا بحاجة لمعرفة احداقية الكرة في كل لحظة اذن سنستقبها كعامل من الدالة الاساسية
if (y + height / 2 > ball_y) {//الجزء المشروح في الرسم السابق
y -= speed;
}
else {
y += speed;
}
Movement_limit();// ننادي على الدالة فقط للتاكد من ارتطام الحواف
}
};
النتيجة :
- يبقى لنا فقط تحديد الإرتطام مع المجذاف
C++:
CheckCollisionCircleRec(Vector2 center,float raduis,Rectangle rec);
// للدائرة x,yهذه الدلة تتحق من ارتطام الدائرة بالمستطيل تاخذ ال عامل هما
// العامل الثاني تاخذ نصف قطر الدائرة
//العامل الاخير تاخذ خصائص المستطيل
CheckCollisionCircleRec(Vector2 {ball.x,ball.y},ball.radius,Rectangle{player.x,player.y,player.width,player.height});
C++:
if (CheckCollisionCircleRec(Vector2{ ball.x,ball.y }, ball.radius, Rectangle{ player.x,player.y,player.width,player.height })) {
ball.speed_x *= -1;
}
if (CheckCollisionCircleRec(Vector2{ ball.x,ball.y }, ball.radius, Rectangle{cpu.x,cpu.y,cpu.width,cpu.height })) {
ball.speed_x *= -1;
}
- بالنسبة لتسجيل النقاط (Scoring)
C++:
//سنقوم بنتعريف متغييرن
int scorePlayer=0,scoreCpu=0;//في البداية رصيد كلا اللاعبين0
// متى ياخذ اللعب نقطة اذا تحقق شرط الارتطام مع جانبي الحائط الذي كتبناه سابقا اذن
if (x + radius >= GetScreenWidth()|| x - radius <= 0) {
speed_x *= -1;
}
//--->سنقوم بفصل الشرط المركب الى شرطين
if (x + radius >= GetScreenWidth()) {
scoreCpu++;
}
if( x - radius <= 0) {
scorePlayer++;
}
///////////////////////////////////
DrawText(char text, int posX, int posY, int fontsize, color);//<---- الدالة المسؤولة عن العرض على الشاشة
كذلك نحن بحاجة لإرجاع الكرة إلى منتصف الملعب في كل مرة تترطم بإحدى الجانبين سيكون اختيار اتجاه الكرة عشوائي .
C++:
//Ballننشئ هذه الدالة داخل كلاس
//ونقوم باستعمالها في كل مرة تترطم الكرة في احدى الجانبين
void Return_Ball() {
x = GetScreenWidth() / 2;
y = GetScreenHeight() / 2;
int orientation[2] = { -1,1 };//ناخذ اتجاهين فقط
speed_x *= orientation[GetRandomValue(0, 1)];// هذه الدالة تقوم باختيار قيمة عشوائية من احدى قيم الجدول يا اما 1 يا اما -1
speed_y *= orientation[GetRandomValue(0, 1)];
}
النتيجة :
وهكذا قمنا بالمرور على كامل النقاط تقريبًا يتبقى لنا فقط التصميم وعداد الوقت.
تلخيص الكود:
C++:
#include <iostream>
#include<raylib.h>
int scorePlayer = 0, scoreCpu = 0;
class Ball {
public:
float x, y;
int speed_x, speed_y;
int radius;
void Draw() {
DrawCircle(x,y, radius, WHITE);
}
void Move() {
x += speed_x;
y+= speed_y;
if (y + radius >= GetScreenHeight() || y-radius<=0) {
speed_y *= -1;
}
if (x + radius >= GetScreenWidth()) {
scoreCpu++;
Return_Ball();
}
if( x - radius <= 0) {
scorePlayer++;
Return_Ball();
}
}
void Return_Ball() {
x = GetScreenWidth() / 2;
y = GetScreenHeight() / 2;
int orientation[2] = { -1,1 };
speed_x *= orientation[GetRandomValue(0, 1)];
speed_y *= orientation[GetRandomValue(0, 1)];
}
};
class Paddle {
protected:
void Movement_limit() {
if (y <= 0) {
y = 0;
}
if (y + height >= GetScreenHeight()) {
y = GetScreenHeight() - height;
}
}
public:
float x, y,width, height;
int speed;
void Draw() {
DrawRectangle(x, y, width, height, WHITE);
}
void Move() {
if (IsKeyDown(KEY_UP)) {
y -= speed;
}
if (IsKeyDown(KEY_DOWN)) {
y += speed;
}
Movement_limit();
}
};
class CPU_paddle :public Paddle{
public:
void Move(int ball_y) {
if (y + height / 2 > ball_y) {
y -= speed;
}
else {
y += speed;
}
Movement_limit();
}
};
Ball ball;
Paddle player;
CPU_paddle cpu;
int main()
{
const int screen_width = 1270;
const int screen_height = 550;
InitWindow(screen_width, screen_height, "Sh3ll Game");
SetTargetFPS(65);
ball.x = screen_width / 2;
ball.y= screen_height / 2;
ball.radius = 20;
ball.speed_x = 5;
ball.speed_y = 5;
player.width = 35;
player.height = 120;
player.x = screen_width - player.width - 10;
player.y = screen_height / 2 - player.height / 2;
player.speed = 7;
cpu.width = 35;
cpu.height = 120;
cpu.x = 10;
cpu.y = screen_height / 2 - cpu.height / 2;
cpu.speed = 6;
while (!WindowShouldClose()) {
BeginDrawing();
ball.Move();
player.Move();
ClearBackground(BLACK);
if (CheckCollisionCircleRec(Vector2{ ball.x,ball.y }, ball.radius, Rectangle{ player.x,player.y,player.width,player.height })) {
ball.speed_x *= -1;
}
if (CheckCollisionCircleRec(Vector2{ ball.x,ball.y }, ball.radius, Rectangle{ cpu.x,cpu.y,cpu.width,cpu.height })) {
ball.speed_x *= -1;
}
DrawLine(screen_width / 2, 0, screen_width / 2, screen_height, WHITE);
ball.Draw();
cpu.Draw();
player.Draw();
cpu.Move(ball.y);
DrawText(TextFormat("%i",scoreCpu), screen_width / 4 - 20, 20, 80, WHITE);
DrawText(TextFormat("%i", scorePlayer), 3*screen_width / 4 - 20, 20, 80, WHITE);
EndDrawing();
}
CloseWindow();
return 0;
}
دمتم بخير
