الشبكة العربية لمطوري الألعاب

محترف  انس مشاركة 1

السلام عليكم.

سؤالي اليوم عن طرق الـ Scrolling المعروف في عدة العاب اشهرها Mario Bros .

انا الان بصدد كتابة برنامج يسمح بمحاكات هذه التقنية، و اود ان اعرف ان كان احدكم قد مر عليها عساه يفيدنا ببعض الحيل او التحسينات.

هل من مجرب لهذه التقنية ؟

شكرا جزيلا سلام.

مبتدئ  محمد يامن سرايجي مشاركة 2

أخي الكريم , اذا كنت تقصد بال Scrolling تحريك الخلفية للعبة فهي بسيطة
ابسطها ان تتلاعب بقيم ال UV لل Texture لمحاكاة هذا الانسحاب

الله أكبر

خبير مدير وسام البهنسي مشاركة 3

ما الطريقة التي تبني منها عالمك؟ هل هو مبني من خلايا متجاورة؟ أم هو صورة كبيرة واحدة؟ أم عدة صور متجاورة ذات أحجام مختلفة؟

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

محترف  انس مشاركة 4

انا استخدم الخلايا المتجاورة لانني قرات انها اكثر فعالية و حفاظا على  موارد الكمبيوتر.

لهذا الغرض استخدم بنية بسيطة (Structure )  تمثل الخانات و هي كالتالي :

struct TILE {

   BITMAP* Img ;
   Rect Pos ;
   int Col_Type ;

} ;

ثم بنية اخرى تمثل الخريطة ككل :

struct MAP {
TILE** Map_Tile ;

int        Tile_Number  ;

int        X_Size       ;
int        Y_Size       ;

} ;
المشكلة التي اواجهها هي كيفية عرض الخريطة الطريقة التي وصلت اليها ليست فعالة لانني رايت من توصل الى نتاج افضل مما وصلت اليه بكود ابسط بكثير.
طريقتي تعتمد على الاحداثيات  للاعب في الشاشة و ليس الخريطة. اقوم بتحويلها الى خانة من خانات الخريطة.و ذلك كي يبدا الرسم من مكان اللاعب. لكن  استعمل شعاعا و الذي يمثل البعد بين اللاعب و بين النقطة التي اريد ان يبدأ بها الرسم.

اظن ان الكود سيتكلم عن نفسه، لكنه قد يكون غير قابل للقراءة ☺ .
void Show_Map ( MAP Map ,SDL_OBJECT Player ,SDL_Surface* ecran  ) {

 int i = 0 ;
 int j = 0 ;
 int a = 0 ;
 int b = 0 ;

int beginx= (Player.Pos.x /TileSizeX  )-5 ;
int beginy= (Player.Pos.y /TileSizeY  )-5 ;

int endx= beginx + 15 ;
int endy= beginy + 15 ;


if ( beginx <0) beginx =0;
if ( beginy <0) beginy =0;

if ( beginx >= MapSizeX) { endx =MapSizeX; beginx = endx-15; }
if ( beginy >= MapSizeY) { endy =MapSizeY; beginy = endy-15; }




for ( i = 0 ; i < MapSizeY ; i ++ ) {
for ( j = 0 ; j < MapSizeX ; j ++ ) {

if( b > 20) b = 0 ;
Map.Map_Tile[ i ] [j ].Pos.x = (b*32);
Map.Map_Tile[ i ] [j ].Pos.y = (a*32);

b++ ;

         }
if( a > 15) a = 0 ;
a++ ;

      }

for ( i =beginy; i <  (beginy+15) ; i++ ){

    for ( j = beginx ; j < (beginx+20); j ++ ) {



if( b > 20) b = 0 ;

Map.Map_Tile[ i ] [j ].Pos.x = (b*32);
Map.Map_Tile[ i ] [j ].Pos.y = (a*32);
Fast_Blit(  Map.Map_Tile[i] [j ].Tile_Img_x ,  Map.Map_Tile[i] [j ].Tile_Img_y ) ;

b++ ;

         }
if( a > 15) a = 0 ;
a++ ;

      }

}
النتيجة للكود في الصورة التي بهل خريطة مخربة.
لقد تحصلت سابقا على نتيجة مقبولة. لكن الكود كان مكتوبا بطريقة غير فعالة لهذا اريد اعادة كتابته. (اخجل حتى من اريكم الكود الخاص بالمحاولة الاولى ☺ ).

شكرا.

خبير مدير وسام البهنسي مشاركة 5

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



const int kTileSizeX = 25; // بالبكسل
const int kTileSizeY = 25; // بالبكسل
const int kTilesInScreenX = 20; // عدد الخلايا المرئية بالشاشة الواحدة
const int kTilesInScreenY = 15; // عدد الخلايا المرئية بالشاشة الواحدة
const int kPlayerTileX = 10; // موقع الشاشة حيث يظهر اللاعب - بالخلية
const int kPlayerTileY = 5; // موقع الشاشة حيث يظهر اللاعب - بالخلية
 
struct TILE
{
  BITMAP* Img;
  //Rect Pos; لا داعي للاحتفاظ بهذه المعلومة
  int Col_Type;
};
 
struct MAP
{
  TILE** Map_Tile;
  //int Tile_Number; يمكن حسابه من ضرب طول الخريطة بعرضها
  int X_Size;
  int Y_Size;
};
 
// إجراء مساعد لحساب حدود الرسم على الشاشة لأي من الأبعاد
static void CalculateVisibleDimension(
  int& begin, int& end, int tileSize, int playerPos,
  int playerTile, int tilesInScreen, int mapSize)
{
  int beginx = (Player.Pos.x/kTileSizeX)-(kTileSizeX-kPlayerTileX); // مبدأ الرسم الأيسر
  if (beginx < 0)
    beginx = 0; // إن اقترب اللاعب من طرف الخريطة الأيسر، فلن نحرك الخريطة أكثر من الحد الأقصى
 
  int endx = beginx + kTilesInScreenX; // مبدأ الرسم الأيمن
  if (endx >= Map.X_Size)
  {
    // إن اقترب اللاعب من طرف الخريطة الأيمن، فلن نحرك الخريطة أكثر من الحد الأقصى
    endx = Map.X_Size;
    beginx = endx-kTilesInScreenX; // عدّل مبدأ الرسم الأيسر مرة أخرى
    if (beginx < 0)
      beginx = 0; // هذا الشرط يتحقق فقط في حال عرض الخريطة أصغر من الشاشة
  }
}
 
void Show_Map(const MAP& Map, SDL_OBJECT* Player, SDL_Surface* ecran)
{
  int beginX, endX, beginY, endY; // حدود الرسم
 
  // حساب حدود الرسم على المحور السيني
  CalculateVisibleDimension(beginX, endX,
    kTileSizeX, Player.Pos.x,
    kPlayerTileX, kTilesInScreenX, Map.X_Size);
 
  // حساب حدود الرسم على المحور الصادي
  CalculateVisibleDimension(beginY, endY,
    kTileSizeY, Player.Pos.y,
    kPlayerTileY, kTilesInScreenY, Map.Y_Size);
 
  // حلقة الرسم
  for (int y=beginY; i<=endY; y++)
  {
    int rectTop = y * kTileSizeY;
    int rectBottom = rectTop + kTileSizeY;
 
    for (int x=beginX; x<endx;>    {
      int rectLeft = x * kTileSizeX;
      int rectRight = rectLeft + kTileSizeX;
      BITMAP* img = Map.Map_Tile[x][y]; // الخلية التي سيتم رسمها
 
      // رسم الخلية في المربع المحدد
      Fast_Blit(img, rectLeft, rectRight, rectTop, rectBottom);
    }
  }
}
 
 
الكود نفسه بالمرفقات أيضاً.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز