English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5'te "Yılan" Oyunu Oluşturma

MQL5'te "Yılan" Oyunu Oluşturma

MetaTrader 5Uzman Danışmanlar | 15 Aralık 2021, 09:45
72 0
MRoVas
[Silindi]

Giriş

Bu makalede, MQL5'te bir "Yılan" oyunu yazma örneğini ele alacağız.

MQL'nin 5. sürümünden bu yana, oyun programlama, öncelikle özel olaylar dahil olmak üzere olay işleme özellikleri sayesinde mümkün hale geldi. Nesne yönelimli programlama, bu tür programların tasarımını basitleştirir, kodu daha net hale getirir ve hata sayısını azaltır.

Bu makaleyi okuduktan sonra, OnChart olay işleme, MQL5 standart kitaplık sınıflarının kullanım örnekleri ve herhangi bir hesaplama yapmak için belirli bir süre sonra işlevin döngüsel çağrılarının alınması hakkında bilgi edineceksiniz.

Oyun Açıklaması

"Yılan" oyunu, uygulamasının basitliği nedeniyle öncelikli örnek olarak seçilmiştir. Programlamaya büyük ilgi gösteren herkes bu oyunu yazabilir.

Wikipedia'ya göre:

Yılan oyunu, ilk olarak 1970'lerin ortalarında oyun salonlarında piyasaya sürülen bir video oyunudur ve o zamandan beri popülerliğini koruyarak bir tür klasik haline gelmiştir.

Oyuncu, etrafı çevrili bir düzlemde dolaşan, yiyecek (veya başka bir eşya) toplayan, kendi kuyruğuna veya oyun alanını çevreleyen "duvarlara" çarpmamaya çalışan, yılana benzeyen uzun, ince bir yaratığı kontrol eder. Alandaki bazı varyasyonlarda ek engeller vardır. Yılan her yemek yediğinde kuyruğu uzar ve oyunu giderek zorlaştırır. Kullanıcı, yılanın kafasının yönünü (yukarı, aşağı, sola veya sağa) kontrol eder ve yılanın vücudu takip eder. Oyuncu, oyun devam ederken yılanın hareket etmesini engelleyemez ve yılanın geri gitmesini sağlayamaz.

MQL5'teki bu "Yılan" uygulamasının bazı sınırlamaları ve özellikleri olacaktır.

Seviye sayısı 6'ya eşittir (0'dan 5'e kadar). Her seviyede 5 can mevcuttur. Tüm canları kullandıktan sonra veya tüm seviyeleri başarıyla geçtikten sonra oyun ilk seviyeye dönecektir. Kendi seviyelerinizi oluşturabilirsiniz. Yılan hızı ve maksimum uzunluğu her seviye için aynıdır.

Oyun alanı 4 öğeden oluşur:

  1. Oyun başlığı. Grafikte oyun konumlandırması için kullanılır. Başlık hareket ettirildiğinde, tüm oyun öğeleri taşınır.
  2. Oyun alanı. Bu, 20x20 boyutlarında bir hücre dizisidir (tablo). Her hücrenin boyutu 20x20 pikseldir. Oyun alanındaki öğeler şunlardır:
    • Yılan. Baş, gövde ve kuyruk olmak üzere en az üç ardışık öğeden meydana gelir. Kafa sola, sağa, yukarı ve aşağı hareket ettirilebilir. Yılana ait diğer tüm öğeler kafadan sonra hareket eder. 
    • Engel. Gri dikdörtgen ile temsil edilir, yılanın kafasının engelle çarpışması durumunda mevcut seviye yeniden başlatılır ve can sayısı 1 sayı azalır.
    • Yiyecek. Yiyecek, dut tarafından sunulur, yılanın yiyecekle çarpışması durumunda yılanın boyutu (gövde uzunluğu) artar. Yılan 12 parça yiyecek yedikten sonra oyunda bir sonraki seviyeye geçilir.
  3. Bilgi Paneli (oyunun durum çubuğu). Üç öğeden oluşur:
    • Seviye. Mevcut seviyeyi gösterir.
    • Kalan yiyecek. Yemek için ne kadar dut kaldığını gösterir.
    • Canlar. Mevcut can sayısını gösterir.
  4. Panel. Üç düğmeden oluşur:
    • "Başlat" düğmesi. Mevcut seviyeyi başlatır.
    • "Duraklat" düğmesi. Oyunu duraklatır.
    • "Durdur" düğmesi. Geçiş başlangıç seviyesinde gerçekleşirken oyunu durdurur.

Tüm bu öğeler Şekil 1'de görülebilir:


Şek. 1. "Yılan" oyununun öğeleri

Oyun başlığı "Düğme" türünde bir nesnedir. Tüm oyun alanı öğeleri, "BmpLabel" türündeki nesnelerdir. Bilgi paneli "Düzenle" türünde üç nesneden oluşur, Kontrol Paneli "Düğme" türünde üç nesneden oluşur. Tüm nesneler, grafiğin sol üst köşesine göre piksel cinsinden X ve Y boyunca mesafelerin tanımına göre konumlandırılır.

Oyun alanının kenarlarının yılanın hareket kabiliyeti için herhangi bir engel teşkil etmediği unutulmamalıdır. Örneğin yılan sol kenardan geçer ve sağ kenarda belirir. Bu, Şekil 2'de görülebilir:


Şekil 2. Yılanın oyun alanının kenarından geçişi

Yılanın kafası ve kuyruğu, gövdesinin aksine döndürülebilir. Kafanın yönü, yılanın hareket yönüne veya komşu öğelerinin pozisyonuna göre belirlenir. Kuyruğun yönü yalnızca komşu öğenin pozisyonuna göre belirlenir. 

Örneğin komşu kuyruk öğesi sol tarafta ise kuyruk sola döner. Kafada ise durum biraz farklıdır. Komşu öğesi sağ tarafta ise kafa sola çevrilir. Kafa ve kuyruk yönlerine ilişkin örnekler aşağıdaki şekillerde gösterilmiştir. Komşu öğelerine göre kafanın ve kuyruğun dönüşüne dikkat edin.





Kafa ve kuyruk sola yönlendirilmiş Kafa ve kuyruk sağa yönlendirilmiş Kafa ve kuyruk aşağı doğru yönlendirilmiş Kafa ve kuyruk aşağı yukarı yönlendirilmiş


Yılan hareketi üç aşamada gerçekleştirilir:

  1. Yöne bağlı olarak hücre kafası sağa, sola, yukarı veya aşağı hareket eder.
  2. Yılanın gövdesinin son öğesinin bir önceki kafa yerindeki hareketi.
  3. Yılanın kuyruğunun son gövde öğesinin önceki yerine hareket ettirmek. Yılanın kuyruğunun yılanın son gövde öğesinin önceki yerine hareketi.

Yılan yiyeceği yerse kuyruk hareket etmez. Bunun yerine, yılanın son vücut öğesinin geçmiş konumuna taşınan, vücudun yeni bir öğesi oluşturulur. 

Sola doğru yılan hareketinin bir örneği aşağıdaki şekillerde gösterilmiştir:





İlk pozisyon  Sol baş hareketine bir hücre
Son gövde öğesinin hareketi
başın önceki yerine hareketi
Son gövde öğesinin son
noktasında kuyruk hareketi

Teori 

Daha sonra oyun yazarken kullanılan araç ve teknikleri ele alacağız.

MQL5 Standart Kitaplığı

Aynı türdeki nesne dizilerini (örneğin, oyun alanı hücreleri, yılan öğeleri) işlemek (oluşturmak, taşımak, silmek) için kullanmak uygundur. Bu diziler ve nesneler, MQL5 Standart Kitaplık sınıfları kullanılarak uygulanabilir.

MQL5 Standart Kitaplık sınıflarının kullanımı, program yazma sürecini basitleştirmeye olanak tanır. Oyun için aşağıdaki kitaplık sınıflarını kullanacağız:

  1. CArrayObj, veri düzenlemesi (dinamik işaretçiler dizisi) için sınıftır.
  2. CChartObjectEdit, CChartObjectButton, CChartObjectBmpLabel sırasıyla "Düzenleme", "Düğme" ve "BmpLabel" öğelerini temsil eden kontrol sınıflarıdır.

MQL5 Standart Kitaplık sınıflarını kullanmak için, aşağıdaki derleyici yönergesini kullanarak bunları dahil etmek gerekir:

#include <path_to_the_file_with_classes_description>

Örneğin, CChartObjectButton türündeki nesneleri kullanmak için şunu yazmamız gerekir:

#include <ChartObjects\ChartObjectsTxtControls.mqh>

Dosya yolları MQL5 Referansı içinde bulunabilir.

MQL5 Standart Kitaplık sınıflarıyla çalışırken bazılarının birbirini devraldığını anlamak önemlidir. Örneğin, CChartObjectButton sınıfı CChartObjectEdit sınıfını devralır, buna karşılık CChartObjectEdit sınıfı CChatObjectLabel sınıfını devralır, vb. Bu, türetilmiş sınıflar için üst sınıf özelliklerinin ve yöntemlerinin kullanılabileceği anlamına gelir.

MQL5 Standart Kitaplık sınıflarını kullanmanın avantajlarını anlamak için, bir düğme oluşturma örneğini ele alalım ve bunu iki şekilde (sınıflar olmadan ve sınıfların kullanımıyla) uygulayalım.

Sınıfların kullanılmadığı bir örnek şu şekildedir:

//Creating a button with name "button"
ObjectCreate(0,"button",OBJ_BUTTON,0,0,0);
//Specifying the text on the button
ObjectSetString(0,"button",OBJPROP_TEXT,"Button text");
//Specifying the button size
ObjectSetInteger(0,"button",OBJPROP_XSIZE,100);
ObjectSetInteger(0,"button",OBJPROP_YSIZE,20);
//Specifying the button position
ObjectSetInteger(0,"button",OBJPROP_XDISTANCE,10);
ObjectSetInteger(0,"button",OBJPROP_YDISTANCE,10);

Sınıfların kullanıldığı bir örnek şu şekildedir:

CChartObjectButton *button;
//Creating an object of CChartObjectButton class and assign a pointer to the button variable
button=new CChartObjectButton;
//Creating a button with properties (in pixels): (width=100, height=20, positions: X=10,Y=10)
button.Create(0,"button",0,10,10,100,20);
//Specifying the text on the button
button.Description("Button text");

Görüldüğü gibi, sınıflarla çalışmak daha kolay. Ayrıca, sınıf nesneleri dizilerde saklanabilir ve kolaylıkla işlenebilir.

Nesne kontrolü sınıflarının yöntemleri ve özellikleri Standart Kitaplık sınıflarına MQL5 Referansı'nda iyi ve net bir şekilde açıklanmıştır.

Nesneler dizisini düzenlemek için Standart Kitaplığın CArrayObj sınıfını kullanacağız; bu, kullanıcının birçok rutin işlemi (yeni bir öğe eklerken dizinin yeniden boyutlandırılması, dizideki nesnelerin silinmesi vb.) yapmaktan kurtulmasına olanak tanır.

CArrayObj Sınıfı Özellikleri

CArrayObj sınıfı, CObject sınıfı türündeki nesnelere dinamik bir işaretçi dizisi düzenlemeye olanak tanır. CObject, Standart Kitaplığın tüm sınıfları için bir üst sınıftır. Bu, tge Standard Kitaplık sınıflarının herhangi bir sınıfının nesnelerine dinamik bir işaretçiler dizisi oluşturabileceğimiz anlamına gelir. Kendi sınıfınızdan dinamik bir nesne dizisi oluşturmanız gerekiyorsa bu, CObject sınıfından devralınmalıdır.

Aşağıdaki örnekte, özel sınıf CObject sınıfının ardılı olduğu için derleyici hataları yazdırmayacaktır:

#include <Arrays\ArrayObj.mqh>
class CMyClass:public CObject
  {
   //fields and methods
  };
//creating an object of CMyClass type and assign it to the value of the my_obj variable
CMyClass *my_obj=new CMyClass;
//declaring a dynamic array of object pointers
CArrayObj array_obj;
//adding the my_obj object pointer at the end of the array_obj array
array_obj.Add(my_obj);

Bir sonraki durumda, my_obj, CObject sınıfına bir işaretçi veya CObject sınıfını devralan bir sınıf olmadığı için derleyici bir hata üretecektir:

#include <Arrays\ArrayObj.mqh>
class CMyClass
  {
   //fields and methods
  };
//creating an object of CMyClass type and assing it to the value of the my_obj variable
CMyClass *my_obj=new CMyClass;
//declaring a dynamic array of object pointers
CArrayObj array_obj;
//adding the my_obj object pointer at the end of the array_obj array
array_obj.Add(my_obj);

Oyun yazarken CArrayObj sınıfının aşağıdaki yöntemlerini kullanacaktır:

  • Add - Dizinin sonuna bir öğe ekler.
  • Insert - Dizinin belirtilen konumuna bir öğe ekler.
  • Detach - Belirtilen konumdaki öğeyi siler (öğe diziden kaldırılır).
  • Total - Dizideki öğe sayısını alır.
  • At - Öğeyi belirtilen konumda alır (öğe diziden kaldırılmaz).

CArrayObj sınıfıyla çalışılmasına ilişkin bir örnek şu şekildedir:

#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CMyClass:public CObject
  {
   public:
   char   s;
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void MyPrint(CArrayObj *array_obj)
  {
   CMyClass *my_obj;
   for(int i=0;i<array_obj.Total();i++)
     {
      my_obj=array_obj.At(i);
      printf("%C",my_obj.s);
     }
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //creating the pointer to the object of CArrayObj class
   CArrayObj *array_obj=new CArrayObj();
   //declaring the CMyClass object pointer
   CMyClass *my_obj;
   //filling the array_obj dynamic array
   for(int i='a';i<='c';i++)
     {
      //creating the CMyClass class object
      my_obj=new CMyClass();
      my_obj.s=char(i);
      //adding an object of CMyClass class at the end of the array_obj dynamic array
      array_obj.Add(my_obj);
     }
   //printing result
   MyPrint(array_obj);
   //creating new object of CMyClass class
   my_obj=new CMyClass();
   my_obj.s='d';
   //inserting new element at the first position of the array
   array_obj.Insert(my_obj,1);
   //printing result
   MyPrint(array_obj);
   //detaching the element from the third position of the array
   my_obj=array_obj.Detach(2);
   //printing result
   MyPrint(array_obj);
   //deleting the dynamic array and all objects with pointers of the array
   delete array_obj;
   return(0);
  }

Bu örnekte, OnInit işlevi, üç öğeli dinamik bir dizi oluşturur. Dizi içeriğinin çıktısı MyPrint işlevinin çağrısıyla gerçekleştirilir.

Dizi Add yöntemi kullanılarak doldurulduktan sonra içeriği (a, b, c) olarak gösterilebilir. 

Insert yöntemi uygulandıktan sonra dizinin içeriği (a, d, b, c) olarak gösterilebilir.

Son olarak, Detach yöntemi uygulandıktan sonra dizi (a, d, c) gibi görünecektir.

delete işleci array_obj değişkenine uygulandığında, yalnızca array_obj dizisini değil, aynı zamanda işaretçileri bunun içinde saklanan nesneleri de kaldıran CArrayObj sınıf yıkıcısı çağrılır. Bunu önlemek için delete komutunu uygulamadan önce CArrayObj sınıfının bellek yönetimi bayrağı false olarak ayarlanmalıdır. Bu bayrak FreeMode yöntemiyle ayarlanır.

Dinamik nesne işaretçileri dizisini kaldırırken işaretçileri dinamik dizide saklanan nesneleri silmek gerekmiyorsa aşağıdaki kodu yazmalısınız:

array_obj.FreeMode(false);
delete array_obj;

Olay İşleme

Oluşturulan olaylar kümesi varsa bunlar kuyrukta birikir ve ardından olay işleme işlevine tutarlı bir şekilde ulaşır.

Grafikle çalışırken oluşturulan olay işleme ve özel olaylar için MQL5, OnChartEvent işlevine sahiptir. Her olayın, OnChartEvent işlevine iletilen bir tanımlayıcısı ve parametreleri vardır. 

OnChartEvent işlevi, yalnızca ileti dizisi diğer tüm program işlevlerinin dışında olduğunda çağrılır. Bu nedenle, aşağıdaki örnekte, OnChartEvent hiçbir zaman kontrolü almayacaktır.

#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void MyFunction()
  {
   CChartObjectButton *button;
   button=new CChartObjectButton;
   button.Create(0,"button",0,10,10,100,20);
   button.Description("Button text");
   while(true)
     {
      //The code, that should be called periodically
     }
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   MyFunction();
   return(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                const long &lparam,
                const double &dparam,
                const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="button") Alert("Button click");
  }

Sonsuz bir while döngüsü, MyFunction işlevinden geri dönmeye izin vermez. OnChartEvent işlevi kontrolü alamaz. Bu nedenle, düğmeye basıldığında Alert işlevi çağrılmaz.

Olay İşleme ile Periyodik Kod Yürütme

Oyunda belirli bir zaman aralığından sonra olayları işleyebilme özelliği ile yılan hareketi işlevinin periyodik çağrısına ihtiyaç vardır. Ancak yukarıda gösterildiği gibi, sonsuz bir döngü OnChartEvent işlevinin çağrılmadığı ve olay işlemenin imkansız hale geldiği gerçeğine yol açar.

Bu nedenle, periyodik kod yürütmenin başka bir yolunu bulmak gerekiyor.

OnTimer'ı Kullanma

MQL5 dili, önceden tanımlanmış saniye sayısına göre periyodik olarak çağrılan özel bir OnTimer işlevine sahiptir. Bunu yapmak için, EventSetTimer işlevini kullanacağız.

Önceki örnek şu şekilde yeniden yazılabilir:

#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void MyFunction()
  {
   //The code, that should be executed periodically
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CChartObjectButton *button;
   button=new CChartObjectButton;
   button.Create(0,"button",0,10,10,100,20);
   button.Description("Button text");
   EventSetTimer(1);
   return(0);
  }
void OnTimer()
{
   MyFunction();
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="button") Alert("Button click");
  }

OnInit işlevinde, düğme oluşturulur ve bir saniyeye eşit bir süre tanımlanır (OnTimer işlevinin çağrısı için), OnTimer işlevinin çağrısı her saniye yapılır, OnTimer işlevi kodu (MyFunction) çağırır; bu, periyodik olarak yürütülmelidir.

OnTimer işlevinin çağrısının saniyenin katı olduğuna dikkat edin. Belirtilen sayıda milisaniyeden sonra işlevi çağırmak için diğer yöntem gereklidir. Bu yöntem, özel olayların kullanılmasıdır.

Özel Olayları Kullanma

Özel olay, EventChartCustom işlevi tarafından oluşturulur, olay kimliği ve parametreleri, EventChartCustom işlevinin giriş parametrelerinde tanımlanır. Özel tanımlı kimliklerin sayısı en fazla 65536 olabilir - 0 ila 65535 aralığında. MQL5 derleyicisi, özel olayları diğer olay türlerinden ayırt etmek için kimliğe otomatik olarak CHARTEVENT_CUSTOM sabit tanımlayıcısını ekler. Bu nedenle, özel kimliklerin gerçek aralığı CHARTEVENT_CUSTOM ile CHARTEVENT_CUSTOM+65535 ( CHARTEVENT_CUSTOM_LAST ) arasındadır.

Özel olayları kullanan bir MyFunction periyodik çağrısı örneği aşağıda sunulmuştur:

#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void MyFunction()
  {
   //The code, that should be executed periodically
   Sleep(200);
   EventChartCustom(0,0,0,0,"");
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CChartObjectButton *button;
   button=new CChartObjectButton;
   button.Create(0,"button",0,10,10,100,20);
   button.Description("Button text");
   MyFunction();
   return(0);
  }
//+------------------------------------------------------------------+
//| OnChartEvent processing function                                 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="button") Alert("Button click");
   if(id==CHARTEVENT_CUSTOM) MyFunction();
  }

Bu örnekte, MyFunction işlevinden önce 200 ms'lik bir gecikme vardır (bu işlevin periyodik çağrı süresi) ve özel olay oluşturulur. OnChartEvent işlevi, tüm olayları işler, özel olay durumunda, MyFunction işlevini tekrar çağırır. Böylece, MyFunction işlevinin periyodik çağrısı bu şekilde uygulanır ve çağrı dönemini milisaniyeye eşitlemek mümkündür.

Uygulama Kısmı

Bir "Yılan" oyunu yazma örneğini ele alalım.

Sabitler ve Düzeyler Haritasını Tanımlama

Harita düzeyleri ayrı bir içerme (üstbilgi) dosyası "Snake.mqh" ve üç boyutlu bir dizi seviyesidir [6] [20] [20]. Seviye haritası ayrı bir "Snake.mqh" üstbilgi dosyasında bulunur ve bir game_level[6][20][20] üç boyutlu dizisi olarak temsil edilir. Bu dizinin her bir öğesi, bireysel düzeyin açıklamasını içeren iki boyutlu bir dizidir. Bir öğenin değeri 9'a eşitse, bu bir engeldir. Bir dizi öğesinin değeri 1,2 veya 3'e eşitse bu, oyun alanındaki ilk pozisyonunu tanımlayan sırasıyla yılanın kafası, gövdesi veya kuyruğudur. Seviye dizisine yeni seviyeler ekleyebilir veya mevcut seviyeleri değiştirebilirsiniz.

Ayrıca, "Snake.mqh" dosyası oyunda kullanılan sabitleri içerir. Örneğin, SPEED_SNAKE ve MAX_LENGTH_SNAKE sabitlerini değiştirerek, her seviyede yılanın hızını ve maksimum uzunluğunu artırabilir/azaltabilirsiniz. Tüm sabitler yorumlanır.

//+------------------------------------------------------------------+
//|                                                        Snake.mqh |
//|                                        Copyright Roman Martynyuk |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Roman Martynyuk"
#property link      "http://www.mql5.com"
#include <VirtualKeys.mqh>                                                    //File with keycodes
#include <Arrays\ArrayObj.mqh>                                                //File with CArrayObj class
#include <ChartObjects\ChartObjectsBmpControls.mqh>                           //File with CChartObjectBmpLabel class
#include <ChartObjects\ChartObjectsTxtControls.mqh>                           //File with CChartObjectButton and CChartObjectEdit classes
#define CRASH_NO                          0                                   //No crash
#define CRASH_OBSTACLE_OR_SNAKE           1                                   //Crash with an "Obstacle" or snake body
#define CRASH_FOOD                        2                                   //Crash with a "Food""
#define DIRECTION_LEFT                    0                                   //Left
#define DIRECTION_UP                      1                                   //Up
#define DIRECTION_RIGHT                   2                                   //Right
#define DIRECTION_DOWN                    3                                   //Down
#define COUNT_COLUMNS                     ArrayRange(game_level,2)            //Number of columns of playing field
#define COUNT_ROWS                        ArrayRange(game_level,1)            //Number of rows of playing field
#define COUNT_LEVELS                      ArrayRange(game_level,0)             //Number of levels
#define START_POS_X                       0                                   //Starting X position of the game
#define START_POS_Y                       0                                   //Starting Y position of the game
#define SQUARE_WIDTH                      20                                  //Square (cell) width (in pixels)
#define SQUARE_HEIGHT                     20                                  //Square (cell) height (in pixels)
#define IMG_FILE_NAME_SQUARE              "\\Images\\Games\\Snake\\square.bmp"          //Path to the "Square" image
#define IMG_FILE_NAME_OBSTACLE            "\\Images\\Games\\Snake\\obstacle.bmp"        //Path to the "Obstacle" image
#define IMG_FILE_NAME_SNAKE_HEAD_LEFT     "\\Images\\Games\\Snake\\head_left.bmp"       //Path to the snake's head (left) image
#define IMG_FILE_NAME_SNAKE_HEAD_UP       "\\Images\\Games\\Snake\\head_up.bmp"         //Path to the snake's head (up) image
#define IMG_FILE_NAME_SNAKE_HEAD_RIGHT    "\\Images\\Games\\Snake\\head_right.bmp"      //Path to the snake's head (right) image
#define IMG_FILE_NAME_SNAKE_HEAD_DOWN     "\\Images\\Games\\Snake\\head_down.bmp"       //Path to the snake's head (down) image
#define IMG_FILE_NAME_SNAKE_BODY          "\\Images\\Games\\Snake\\body.bmp"            //Path to the snake's body image
#define IMG_FILE_NAME_SNAKE_TAIL_LEFT     "\\Images\\Games\\Snake\\tail_left.bmp"       //Path to the snake's tail (left) image
#define IMG_FILE_NAME_SNAKE_TAIL_UP       "\\Images\\Games\\Snake\\tail_up.bmp"         //Path to the snake's tail (up) image
#define IMG_FILE_NAME_SNAKE_TAIL_RIGHT    "\\Images\\Games\\Snake\\tail_right.bmp"      //Path to the snake's tail (right) image
#define IMG_FILE_NAME_SNAKE_TAIL_DOWN     "Games\\Snake\\tail_down.bmp"       //Path to the snake's tail (down) image
#define IMG_FILE_NAME_FOOD                "Games\\Snake\food.bmp"             //Path to the "Food" image
#define SQUARE_BMP_LABEL_NAME             "snake_square_%u_%u"                //Name of the "Square" graphic label
#define OBSTACLE_BMP_LABEL_NAME           "snake_obstacle_%u_%u"              //Name of the "Obstacle" graphic label
#define SNAKE_ELEMENT_BMP_LABEL_NAME      "snake_element_%u"                  //Name of the "Snake" graphic label
#define FOOD_BMP_LABEL_NAME               "snake_food_%u"                     //Name of the "Food" graphic label
#define LEVEL_EDIT_NAME                   "snake_level_edit"                  //Name of the "Level" edit
#define LEVEL_EDIT_TEXT                   "Level: %u of %u"                   //Text of the "Level" edit
#define FOOD_LEFT_OVER_EDIT_NAME          "snake_food_available_edit"         //Name of the "Food left" edit
#define FOOD_LEFT_OVER_EDIT_TEXT          "Food left over: %u"                //Text of the "Food left" edit
#define LIVES_EDIT_NAME                   "snake_lives_edit"                  //Name of the "Lives" edit
#define LIVES_EDIT_TEXT                   "Lives: %u"                         //Text of the "Lives" edit
#define START_GAME_BUTTON_NAME            "snake_start_game_button"           //Name of the "Start" button
#define START_GAME_BUTTON_TEXT            "Start"                             //Text of the "Start" button
#define PAUSE_GAME_BUTTON_NAME            "snake_pause_game_button"           //Name of the "Pause" button
#define PAUSE_GAME_BUTTON_TEXT            "Pause"                             //Text of the "Pause" button
#define STOP_GAME_BUTTON_NAME             "snake_stop_game_button"            //Name of the "Stop" button
#define STOP_GAME_BUTTON_TEXT             "Stop"                              //Text of the "Stop" button
#define CONTROL_WIDTH                     (COUNT_COLUMNS*(SQUARE_WIDTH-1)+1)/3//Control Panel Width (1/3 of playing field width)
#define CONTROL_HEIGHT                    40                                  //Control Panel Height
#define CONTROL_BACKGROUND                C'240,240,240'                      //Color of Control Panel buttons
#define CONTROL_COLOR                     Black                               //Text Color of Control Panel Buttons
#define STATUS_WIDTH                      (COUNT_COLUMNS*(SQUARE_WIDTH-1)+1)/3//Status Panel Width (1/3 of playing field width)
#define STATUS_HEIGHT                     40                                  //Status Panel Height
#define STATUS_BACKGROUND                 LemonChiffon                        //Status Panel Background Color
#define STATUS_COLOR                      Black                               //Status Panel Text Color
#define HEADER_BUTTON_NAME                "snake_header_button"               //Name of the "Header" button
#define HEADER_BUTTON_TEXT                "Snake"                             //Text of the "Header" button
#define HEADER_WIDTH                      COUNT_COLUMNS*(SQUARE_WIDTH-1)+1    //Width of the "Header" button (playing field width)
#define HEADER_HEIGHT                     40                                  //Height of the "Header" button
#define HEADER_BACKGROUND                 BurlyWood                           //Header Background Color
#define HEADER_COLOR                      Black                               //Headet Text Color
#define COUNT_FOOD                        3                                   //Number of "Foods" at playing field
#define LIVES_SNAKE                       5                                   //Number of snake lives at each level
#define SPEED_SNAKE                       100                                 //Snake Speed (in milliseconds)
#define MAX_LENGTH_SNAKE                  15                                  //Maximal Snake Length
#define MAX_LEVEL                         COUNT_LEVELS-1                      //Maximal Level
int game_level[][20][20]=
  {
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,3,2,1,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
     }
      ,
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
     }
      ,
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,9,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
     }
      ,
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0},
        {0,0,0,0,0,9,9,0,0,0,0,0,3,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
     }
      ,
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,1,2,3,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,9,9,9,9,0},
        {0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
     }
      ,
     {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0},
        {0,1,2,3,0,0,0,0,0,0,0,9,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,9,9,0,0,0,0,0,0,9,9,9,9,0},
        {0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0}
     }
  };
//+------------------------------------------------------------------+

#define SQUARE_BMP_LABEL_NAME "snake_square_% u_% U" sabitinin tanımına dikkat edin. Oyun alanını oluşturacağız. Oyun alanının her hücresi bir bit eşlem etiketidir; bu, benzersiz bir ada sahip olmalıdır. Bir hücrenin adı bu sabit tarafından tanımlanır, bir hücre adının biçim belirtimi %u'dur; bu, işaretli tam sayı anlamına gelir.

BmpLabel'i oluştururken adı şu şekilde belirtecekseniz: StringFormat (SQUARE_BMP_LABEL_NAME, 1,0), ad, "snake_square_1_0" değerine eşit olacaktır.

Sınıflar

Oyun için geliştirilmiş iki özel sınıf vardır, bunlar "Snake.mq5" dosyasında yer alır.

ChartFieldElement sınıfı:

//+------------------------------------------------------------------+
//| CChartFieldElement class                                         |
//+------------------------------------------------------------------+
class CChartFieldElement:public CChartObjectBmpLabel
  {
private:
   int               pos_x,pos_y;
public:
   int               GetPosX(){return pos_x;}
   int               GetPosY(){return pos_y;}
   //setting position (pos_x,pos_y) in internal coordinates
   void              SetPos(int val_pos_x,int val_pos_y)
     {
      pos_x=(val_pos_x==-1)?COUNT_COLUMNS-1:((val_pos_x==COUNT_COLUMNS)?0:val_pos_x);
      pos_y=(val_pos_y==-1)?COUNT_ROWS-1:((val_pos_y==COUNT_ROWS)?0:val_pos_y);
     }
   //conversion of internal coordinates to absolute and object movement on the chart
   void              Move(int start_pos_x,int start_pos_y)
     {
      X_Distance(start_pos_x+pos_x*SQUARE_WIDTH-pos_x+(SQUARE_WIDTH-X_Size())/2);
      Y_Distance(start_pos_y+pos_y*SQUARE_HEIGHT-pos_y+(SQUARE_HEIGHT-Y_Size())/2);
     }
  };

CChartFiledElement sınıfı, CChartObjectBmpLabel sınıfını devralır, böylece onu genişletir. Hücre bariyeri, yılanın kafası, gövdesi ve kuyruğu gibi tüm oyun alanı ve "yiyecek" bu sınıfın nesneleridir. pos_x ve pos_y özellikleri, oyun alanındaki öğelerin göreli koordinatlarıdır; yani öğenin satır ve sütun dizinleridir. SetPos yöntemi bu koordinatları ayarlar. Move yöntemi, göreli koordinatları piksel cinsinden X ve Y eksenleri boyunca mesafelere dönüştürür ve öğeyi taşır. Bunu yapmak için, CChartObjectBmpLabel sınıfının X_Distance ve YDistance yöntemlerini çağırır.

CSnakeGame sınıfı:

//+------------------------------------------------------------------+
//| CSnakeGame class                                                 |
//+------------------------------------------------------------------+
class CSnakeGame
  {
private:
   CArrayObj        *square_obj_arr;                     //Array of playing field cells
   CArrayObj        *control_panel_obj_arr;              //Array of control panel buttons
   CArrayObj        *status_panel_obj_arr;               //Array of control panel edits
   CArrayObj        *obstacle_obj_arr;                   //Array of an obstacles
   CArrayObj        *food_obj_arr;                       //Array of "Food"
   CArrayObj        *snake_element_obj_arr;              //Array of snake elements
   CChartObjectButton *header;                           //Header
   int               direction;                          //Snake movement direction
   int               current_lives;                      //Number of snake Lives
   int               current_level;                      //Level
   int               header_left;                        //Left position of a header (X)
   int               header_top;                         //Top position of a header (Y)
public:
   //class constructor
   void              CSnakeGame()
     {
      current_lives=LIVES_SNAKE;
      current_level=0;
      header_left=START_POS_X;
      header_top=START_POS_Y;
     }
   //method for definition of header_left and header_top fields
   void              SetHeaderPos(int val_header_left,int val_header_top)
     {
      header_left=val_header_left;
      header_top=val_header_top;
     };
   //Get/Set direction methods
   void              SetDirection(int d){direction=d;}
   int               GetDirection(){return direction;}
   //Header creation and deletion methods
   void              CreateHeader();
   void              DeleteHeader();
   //Playing field creation, movement and deletion methods
   void              CreateField();
   void              FieldMoveOnChart();
   void              DeleteField();
   //Obstacle creation, movement and deletion methods
   void              CreateObstacle();
   void              ObstacleMoveOnChart();
   void              DeleteObstacle();
   //Snake creation, movement and deletion methods
   void              CreateSnake();
   void              SnakeMoveOnChart();
   void              SnakeMoveOnField();                 //snake movement on the playing field
   void              SetTrueSnake();                     //setting the images of the current snake's head and tail
   int               Check();                            //check for the collision with the playing field elements
   void              DeleteSnake();
   //Food creation, movement and deletion methods
   void              CreateFood();
   void              FoodMoveOnChart();
   void              FoodMoveOnField(int food_num);
   void              DeleteFood();
   //Status panel creation, movement and deletion methods
   void              CreateControlPanel();
   void              ControlPanelMoveOnChart();
   void              DeleteControlPanel();
   //Control panel creation, movement and deletion methods
   void              CreateStatusPanel();
   void              StatusPanelMoveOnChart();
   void              DeleteStatusPanel();
   //Move all elements on the chart
   void              AllMoveOnChart();
   //Game initialization
   void              Init();
   //Game deinitialization
   void              Deinit();
   //Game control methods
   void              StartGame();
   void              PauseGame();
   void              StopGame();
   void              ResetGame();
   void              NextLevel();
  };

CSnakeGame oyunun ana sınıfıdır; oyun öğelerinin oluşturulması, taşınması ve kaldırılması için alanları ve yöntemleri içerir. Sınıf açıklamasının başında, oyun öğelerinin işaretçilerine ait dinamik dizilerin düzenlenmesi için alanların bildirildiği görülebilir. Örneğin, yılan öğelerinin işaretçileri snake_element_obj_arr alanında saklanır. snake_element_obj_arr dizisinin sıfırıncı dizi indeksi bir yılanın kafası ve sonuncusu ise kuyruğu olacaktır. Bunu bilerek, oyun alanında yılanı kolaylıkla işleyebilirsiniz.

CSnakeGame sınıfının tüm yöntemlerini ele alalım. Yöntemler, bu makalenin "Teori" bölümünde sunulan teoriye dayanarak uygulanır.

Oyun üstbilgisi


//+------------------------------------------------------------------+
//| Header creation method                                           |
//+------------------------------------------------------------------+
void CSnakeGame::CreateHeader(void)
  {
   //creating a new object of CChartObjectButton class and specifying the properties of header of CSnakeGame class
   header=new CChartObjectButton;
   header.Create(0,HEADER_BUTTON_NAME,0,header_left,header_top,HEADER_WIDTH,HEADER_HEIGHT);
   header.BackColor(HEADER_BACKGROUND);
   header.Color(HEADER_COLOR);
   header.Description(HEADER_BUTTON_TEXT);
   //the header is selectable
   header.Selectable(true);
  }
//+------------------------------------------------------------------+
//| Header deletion method                                           |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteHeader(void)
  {
   delete header;
  }

Oyun alanı

//+------------------------------------------------------------------+
//| Playing Field creation method                                    |
//+------------------------------------------------------------------+
void CSnakeGame::CreateField()
  {
   int i,j;
   CChartFieldElement *square_obj;
   //creating an object of CArrayObj class and assign the square_obj_arr properties of CSnakeGame class
   square_obj_arr=new CArrayObj();
   for(i=0;i<COUNT_ROWS;i++)
      for(j=0;j<COUNT_COLUMNS;j++)
        {
         square_obj=new CChartFieldElement();
         square_obj.Create(0,StringFormat(SQUARE_BMP_LABEL_NAME,i,j),0,0,0);
         square_obj.BmpFileOn(IMG_FILE_NAME_SQUARE);
         //specifying the internal coordinates of the cell
         square_obj.SetPos(j,i);
         square_obj_arr.Add(square_obj);
        }
   //moving the playing field cells
   FieldMoveOnChart();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| The movement of playing field cells on the chart                 |
//+------------------------------------------------------------------+
void CSnakeGame::FieldMoveOnChart()
  {
   CChartFieldElement *square_obj;
   int i;
   i=0;
   while((square_obj=square_obj_arr.At(i))!=NULL)
     {
      square_obj.Move(header_left,header_top+HEADER_HEIGHT);
      i++;
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Deletion of a playing field                                      |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteField()
  {
   delete square_obj_arr;
   ChartRedraw();
  }

Engeller

//+------------------------------------------------------------------+
//| Creation of the obstacles                                        |
//+------------------------------------------------------------------+
void CSnakeGame::CreateObstacle()
  {
   int i,j;
   CChartFieldElement *obstacle_obj;
   //creating an object of CArrayObj class and assign the obstacle_obj_arr properties of CSnakeGame class
   obstacle_obj_arr=new CArrayObj();
   for(i=0;i<COUNT_ROWS;i++)
      for(j=0;j<COUNT_COLUMNS;j++)
         if(game_level[current_level][i][j]==9)
           {
            obstacle_obj=new CChartFieldElement();
            obstacle_obj.Create(0,StringFormat(OBSTACLE_BMP_LABEL_NAME,i,j),0,0,0);
            obstacle_obj.BmpFileOn(IMG_FILE_NAME_OBSTACLE);
            //specifying the internal coordinates of the obstacle
            obstacle_obj.SetPos(j,i);
            obstacle_obj_arr.Add(obstacle_obj);
           }
   //moving the obstacle on the chart
   ObstacleMoveOnChart();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Obstacle movement method                                         |
//+------------------------------------------------------------------+
void CSnakeGame::ObstacleMoveOnChart()
  {
   CChartFieldElement *obstacle_obj;
   int i;
   i=0;
   while((obstacle_obj=obstacle_obj_arr.At(i))!=NULL)
     {
      obstacle_obj.Move(header_left,header_top+HEADER_HEIGHT);
      i++;
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Obstacle deletion method                                         |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteObstacle()
  {
   delete obstacle_obj_arr;
   ChartRedraw();
  }

Yılan

//+------------------------------------------------------------------+
//| Snake creation method                                            |
//+------------------------------------------------------------------+
void CSnakeGame::CreateSnake()
  {
   int i,j;
   CChartFieldElement *snake_element_obj,*snake_arr[];
   ArrayResize(snake_arr,3);
   //creating an object of CArrayObj class and assign it to the snake_element_obj_arr properties of CSnakeGame class
   snake_element_obj_arr=new CArrayObj();
   for(i=0;i<COUNT_COLUMNS;i++)
      for(j=0;j<COUNT_ROWS;j++)
         if(game_level[current_level][i][j]==1 || game_level[current_level][i][j]==2 || game_level[current_level][i][j]==3)
           {
            snake_element_obj=new CChartFieldElement();
            snake_element_obj.Create(0,StringFormat(SNAKE_ELEMENT_BMP_LABEL_NAME,game_level[current_level][i][j]),0,0,0);
            snake_element_obj.BmpFileOn(IMG_FILE_NAME_SNAKE_BODY);
            //specifying the internal coordinates of the snake element
            snake_element_obj.SetPos(j,i);
            snake_arr[game_level[current_level][i][j]-1]=snake_element_obj;
           }
   snake_element_obj_arr.Add(snake_arr[0]);
   snake_element_obj_arr.Add(snake_arr[1]);
   snake_element_obj_arr.Add(snake_arr[2]);
   //moving the snake on the chart
   SnakeMoveOnChart();
   //setting the correct images of the snake's head and tail
   SetTrueSnake();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Snake movement on the chart                                      |
//+------------------------------------------------------------------+
void CSnakeGame::SnakeMoveOnChart()
  {
   CChartFieldElement *snake_element_obj;
   int i;
   i=0;
   while((snake_element_obj=snake_element_obj_arr.At(i))!=NULL)
     {
      snake_element_obj.Move(header_left,header_top+HEADER_HEIGHT);
      i++;
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Snake movement on the playing field                              |
//+------------------------------------------------------------------+
void CSnakeGame::SnakeMoveOnField()
  {
   int prev_x,prev_y,next_x,next_y,check;
   CChartFieldElement *snake_head_obj,*snake_body_obj,*snake_tail_obj;
   //getting the snake's head from the array
   snake_head_obj=snake_element_obj_arr.At(0);
   //saving the coordinates of a head
   prev_x=snake_head_obj.GetPosX();
   prev_y=snake_head_obj.GetPosY();
   //setting the new internal coordinates for the head depending on the movement direction
   switch(direction)
     {
      case DIRECTION_LEFT:snake_head_obj.SetPos(prev_x-1,prev_y);break;
      case DIRECTION_UP:snake_head_obj.SetPos(prev_x,prev_y-1);break;
      case DIRECTION_RIGHT:snake_head_obj.SetPos(prev_x+1,prev_y);break;
      case DIRECTION_DOWN:snake_head_obj.SetPos(prev_x,prev_y+1);break;
     }
   //moving the snake's head
   snake_head_obj.Move(header_left,header_top+HEADER_HEIGHT);
   //check for the snake's head collision with the other playing field elements (obstacle, snake body, food)
   check=Check();
   //getting the last element of the snake's body
   snake_body_obj=snake_element_obj_arr.Detach(snake_element_obj_arr.Total()-2);
   //saving coordinates of the snake's body 
   next_x=snake_body_obj.GetPosX();
   next_y=snake_body_obj.GetPosY();
   //moving the snake's body to the previous head's position
   snake_body_obj.SetPos(prev_x,prev_y);
   snake_body_obj.Move(header_left,header_top+HEADER_HEIGHT);
   //saving the previous position of the snake's body
   prev_x=next_x;
   prev_y=next_y;
   //inserting the snake's body to the first position of the snake_element_obj_arr array
   snake_element_obj_arr.Insert(snake_body_obj,1);
   //if the snake's head has collided with the "Food"
   if(check>=CRASH_FOOD)
     {
      //creating new element of the snake's body
      snake_body_obj=new CChartFieldElement();
      snake_body_obj.Create(0,StringFormat(SNAKE_ELEMENT_BMP_LABEL_NAME,snake_element_obj_arr.Total()+1),0,0,0);
      snake_body_obj.BmpFileOn(IMG_FILE_NAME_SNAKE_BODY);
      //moving the body element to the end of the snake before the tail
      snake_body_obj.SetPos(prev_x,prev_y);
      snake_body_obj.Move(header_left,header_top+HEADER_HEIGHT);
      //adding the body to the penultimate position of the snake_element_obj_arr array
      snake_element_obj_arr.Insert(snake_body_obj,snake_element_obj_arr.Total()-1);
      //if snake's body isn't equal to the maximal snake length
      if(snake_element_obj_arr.Total()!=MAX_LENGTH_SNAKE)
        {
         //moving the eaten element on the new place on the playing field
         FoodMoveOnField(check-CRASH_FOOD);
        }
      //else we generate the custom event, that indicates that current snake length is the maximal possible
      else EventChartCustom(0,2,0,0,"");
     }
   //else if there isn't collision with the food, moving the tail to the position of the snake's body
   else
     {
      snake_tail_obj=snake_element_obj_arr.At(snake_element_obj_arr.Total()-1);
      snake_tail_obj.SetPos(prev_x,prev_y);
      snake_tail_obj.Move(header_left,header_top+HEADER_HEIGHT);
     }
   //setting the correct images for the head and tail
   SetTrueSnake();
   ChartRedraw();
   //generating the custom event for periodic call of this snake movement function
   EventChartCustom(0,0,0,0,"");
   Sleep(SPEED_SNAKE);
  }
//+------------------------------------------------------------------+
//| Setting the correct images for the snake's head and tail         |
//+------------------------------------------------------------------+
void CSnakeGame::SetTrueSnake()
  {
   CChartFieldElement *snake_head,*snake_body,*snake_tail;
   int total,x1,x2,y1,y2;
   total=snake_element_obj_arr.Total();
   //getting the snake's head
   snake_head=snake_element_obj_arr.At(0);
   //saving position of a head
   x1=snake_head.GetPosX();
   y1=snake_head.GetPosY();
   //getting the first element of the snake's body
   snake_body=snake_element_obj_arr.At(1);
   //saving coordinates of the body
   x2=snake_body.GetPosX();
   y2=snake_body.GetPosY();
   //choosing the file with an image depening on the position of the head and the first body element relative to each other
   //setting the snake's movement direction depending on the snake's head direction
   if(x1-x2==1 || x1-x2==-(COUNT_COLUMNS-1))
     {
      snake_head.BmpFileOn(IMG_FILE_NAME_SNAKE_HEAD_RIGHT);
      direction=DIRECTION_RIGHT;
     }
   else if(y1-y2==1 || y1-y2==-(COUNT_ROWS-1))
     {
      snake_head.BmpFileOn(IMG_FILE_NAME_SNAKE_HEAD_DOWN);
      direction=DIRECTION_DOWN;
     }
   else if(x1-x2==-1 || x1-x2==COUNT_COLUMNS-1)
     {
      snake_head.BmpFileOn(IMG_FILE_NAME_SNAKE_HEAD_LEFT);
      direction=DIRECTION_LEFT;
     }
   else
     {
      snake_head.BmpFileOn(IMG_FILE_NAME_SNAKE_HEAD_UP);
      direction=DIRECTION_UP;
     }
   //getting the last element of the snake's body
   snake_body=snake_element_obj_arr.At(total-2);
   //saving coordinates of the body
   x1=snake_body.GetPosX();
   y1=snake_body.GetPosY();
   //getting the tail of the snake
   snake_tail=snake_element_obj_arr.At(total-1);
   //saving coordinates of the tail
   x2=snake_tail.GetPosX();
   y2=snake_tail.GetPosY();

   //choosing the file with an image depening on the position of the tail and the last body element relative to each other
   if(x1-x2==1 || x1-x2==-(COUNT_COLUMNS-1))    snake_tail.BmpFileOn(IMG_FILE_NAME_SNAKE_TAIL_RIGHT);
   else if(y1-y2==1 || y1-y2==-(COUNT_ROWS-1))  snake_tail.BmpFileOn(IMG_FILE_NAME_SNAKE_TAIL_DOWN);
   else if(x1-x2==-1 || x1-x2==COUNT_COLUMNS-1) snake_tail.BmpFileOn(IMG_FILE_NAME_SNAKE_TAIL_LEFT);
   else                                         snake_tail.BmpFileOn(IMG_FILE_NAME_SNAKE_TAIL_UP);
  }
//+------------------------------------------------------------------+
//| Check for snake's head collision with the playing field elements |
//+------------------------------------------------------------------+
int CSnakeGame::Check()
  {
   int i;
   CChartFieldElement *snake_head_obj,*snake_element_obj,*obstacle_obj,*food_obj;
   //getting the snake's head
   snake_head_obj=snake_element_obj_arr.At(0);
   i=0;
   //check for the head's collision with the obstacle
   while((obstacle_obj=obstacle_obj_arr.At(i))!=NULL)
     {
      if(snake_head_obj.GetPosX()==obstacle_obj.GetPosX() && snake_head_obj.GetPosY()==obstacle_obj.GetPosY())
        {
         EventChartCustom(0,1,0,0,"");
         return CRASH_OBSTACLE_OR_SNAKE;
        }
      i++;
     }
   i=0;
   //check for the collision of head with the food
   while((food_obj=food_obj_arr.At(i))!=NULL)
     {
      if(snake_head_obj.GetPosX()==food_obj.GetPosX() && snake_head_obj.GetPosY()==food_obj.GetPosY())
        {
         //hiding the food
         food_obj.Background(true);
         return(CRASH_FOOD+i);
        }
      i++;
     }
   i=3;
   //check for the collision of a head with the body and tail
   while((snake_element_obj=snake_element_obj_arr.At(i))!=NULL)
     {
      //we don't check for the collision with the last snake's element, because it hasn't been moved yet
      if(snake_element_obj_arr.At(i+1)==NULL)
         break;
      if(snake_head_obj.GetPosX()==snake_element_obj.GetPosX() && snake_head_obj.GetPosY()==snake_element_obj.GetPosY())
        {
         EventChartCustom(0,1,0,0,"");
         //hiding the snake's element we have collided
         snake_element_obj.Background(true);
         return CRASH_OBSTACLE_OR_SNAKE;
        }
      i++;
     }
   return CRASH_NO;
  }
//+------------------------------------------------------------------+
//| Snake deletion                                                   |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteSnake()
  {
   delete snake_element_obj_arr;
   ChartRedraw();
  }

Yılanın kafası hareket ettirildikten sonra, çarpışma tanımlayıcısını döndüren Check() işleviyle çarpışmayı kontrol eder.

SetTrueSnake() işlevi, komşu öğelerinin pozisyonuna bağlı olarak yılanın kafasının ve kuyruğunun doğru çizimini belirtmek için kullanılır.

Yılan için Yiyecek

//+------------------------------------------------------------------+
//| Food creation                                                    |
//+------------------------------------------------------------------+
void CSnakeGame::CreateFood()
  {
   int i;
   CChartFieldElement *food_obj;
   MathSrand(uint(TimeLocal()));
   //creating an object of CArrayObj class and assign it to the food_obj_arr properties of CSnakeGame class
   food_obj_arr=new CArrayObj();
   i=0;
   while(i<COUNT_FOOD)
     {
      //creating the food
      food_obj=new CChartFieldElement;
      food_obj.Create(0,StringFormat(FOOD_BMP_LABEL_NAME,i),0,0,0);
      food_obj.BmpFileOn(IMG_FILE_NAME_FOOD);
      food_obj_arr.Add(food_obj);
      //setting the field coordinates on the field and moving it on the playing field
      FoodMoveOnField(i);
      i++;
     }
  }
//+------------------------------------------------------------------+
//| Food movement method                                             |
//+------------------------------------------------------------------+
void CSnakeGame::FoodMoveOnChart()
  {
   CChartFieldElement *food_obj;
   int i;
   i=0;
   while((food_obj=food_obj_arr.At(i))!=NULL)
     {
      food_obj.Move(header_left,header_top+HEADER_HEIGHT);
      i++;
     }
   ChartRedraw();
  }
//+---------------------------------------------------------------------------+
//| A method to set coordinates of a food and to move it on the playing field |
//+---------------------------------------------------------------------------+
void CSnakeGame::FoodMoveOnField(int food_num)
  {
   int i,j,k,n,m;
   CChartFieldElement *snake_element_obj,*food_obj;
   CChartObjectEdit *edit_obj;
   //setting a new value for "Foods left" on the status panel
   edit_obj=status_panel_obj_arr.At(1);
   edit_obj.Description(StringFormat(spaces2+FOOD_LEFT_OVER_EDIT_TEXT,MAX_LENGTH_SNAKE-snake_element_obj_arr.Total()));
   bool b;
   b=false;
   k=0;
   //generating randomly the food coordinates until the we get the free cells
   while(true)
     {
      //generating a row number
      i=(int)(MathRand()/32767.0*(COUNT_ROWS-1));
      //generating a column number
      j=(int)(MathRand()/32767.0*(COUNT_COLUMNS-1));
      n=0;
      //check, if there are any elements of the snake
      while((snake_element_obj=snake_element_obj_arr.At(n))!=NULL)
        {
         if(j!=snake_element_obj.GetPosX() && i!=snake_element_obj.GetPosY())
            b=true;
         else
           {
            b=false;
            break;
           }
         n++;
        }
      //checking for the other food presence
      if(b==true)
        {
         n=0;
         while((food_obj=food_obj_arr.At(n))!=NULL)
           {
            if(j!=food_obj.GetPosX() && i!=food_obj.GetPosY())
               b=true;
            else
              {
               b=false;
               break;
              }
            n++;
           }
        }
      //checking for the presence of the obstacle
      if(b==true && game_level[current_level][i][j]!=9) break;
      k++;
     }
   food_obj=food_obj_arr.At(food_num);
   //show food
   food_obj.Background(false);
   //setting new coordinates
   food_obj.SetPos(j,i);
   //moving the food
   food_obj.Move(header_left,header_top+HEADER_HEIGHT);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Food deletion                                                    |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteFood()
  {
   delete food_obj_arr;
   ChartRedraw();
  }

Yiyeceklerin oyun alanındaki konumu, yiyeceğin yerleştirileceği hücre alanında başka öğeler içermemek şartıyla rastgele belirlenir.

Durum Paneli

//+------------------------------------------------------------------+
//| Status Panel Creation                                            |
//+------------------------------------------------------------------+
void CSnakeGame::CreateStatusPanel()
  {
   CChartObjectEdit *edit_obj;  
   //creating an object of CArrayObj class and assign it to the status_panel_obj_arr properties of CSnakeGame class
   status_panel_obj_arr=new CArrayObj();
   //creating the "Level" edit
   edit_obj=new CChartObjectEdit;
   edit_obj.Create(0,LEVEL_EDIT_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   edit_obj.BackColor(STATUS_BACKGROUND);
   edit_obj.Color(STATUS_COLOR);
   edit_obj.Description(StringFormat(spaces6+LEVEL_EDIT_TEXT,current_level,MAX_LEVEL));
   edit_obj.Selectable(false);
   edit_obj.ReadOnly(true);
   status_panel_obj_arr.Add(edit_obj);
   //creating the "Food left over" edit
   edit_obj=new CChartObjectEdit;
   edit_obj.Create(0,FOOD_LEFT_OVER_EDIT_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   edit_obj.BackColor(STATUS_BACKGROUND);
   edit_obj.Color(STATUS_COLOR);
   edit_obj.Description(StringFormat(spaces2+FOOD_LEFT_OVER_EDIT_TEXT,MAX_LENGTH_SNAKE-3));
   edit_obj.Selectable(false);
   edit_obj.ReadOnly(true);
   status_panel_obj_arr.Add(edit_obj);
   //creating the "Lives" edit
   edit_obj=new CChartObjectEdit;
   edit_obj.Create(0,LIVES_EDIT_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   edit_obj.BackColor(STATUS_BACKGROUND);
   edit_obj.Color(STATUS_COLOR);
   edit_obj.Description(StringFormat(spaces8+LIVES_EDIT_TEXT,current_lives));
   edit_obj.Selectable(false);
   edit_obj.ReadOnly(true);
   status_panel_obj_arr.Add(edit_obj);
   //moving the status panel
   StatusPanelMoveOnChart();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Status Panel movement method                                     |
//+------------------------------------------------------------------+
void CSnakeGame::StatusPanelMoveOnChart()
  {
   CChartObjectEdit *edit_obj;
   int x,y,i;
   x=header_left;
   y=header_top+HEADER_HEIGHT+COUNT_ROWS*(SQUARE_HEIGHT-1)+1;
   i=0;
   while((edit_obj=status_panel_obj_arr.At(i))!=NULL)
     {
      edit_obj.X_Distance(x+i*CONTROL_WIDTH);
      edit_obj.Y_Distance(y);
      i++;
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Status Panel deletion method                                     |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteStatusPanel()
  {
   delete status_panel_obj_arr;
   ChartRedraw();
  }

Kontrol Paneli

//+------------------------------------------------------------------+
//| Control Panel creation method                                    |
//+------------------------------------------------------------------+
void CSnakeGame::CreateControlPanel()
  {
   CChartObjectButton *button_obj;
   //creating an object of CArrayObj class and assign it to the control_panel_obj_arr properties of CSnakeGame class
   control_panel_obj_arr=new CArrayObj();
   //creating the "Start" button
   button_obj=new CChartObjectButton;
   button_obj.Create(0,START_GAME_BUTTON_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   button_obj.BackColor(CONTROL_BACKGROUND);
   button_obj.Color(CONTROL_COLOR);
   button_obj.Description(START_GAME_BUTTON_TEXT);
   button_obj.Selectable(false);
   control_panel_obj_arr.Add(button_obj);
   //creating the "Pause" button
   button_obj=new CChartObjectButton;
   button_obj.Create(0,PAUSE_GAME_BUTTON_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   button_obj.BackColor(CONTROL_BACKGROUND);
   button_obj.Color(CONTROL_COLOR);
   button_obj.Description(PAUSE_GAME_BUTTON_TEXT);
   button_obj.Selectable(false);
   control_panel_obj_arr.Add(button_obj);
   //creating the "Stop" button
   button_obj=new CChartObjectButton;
   button_obj.Create(0,STOP_GAME_BUTTON_NAME,0,0,0,CONTROL_WIDTH,CONTROL_HEIGHT);
   button_obj.BackColor(CONTROL_BACKGROUND);
   button_obj.Color(CONTROL_COLOR);
   button_obj.Description(STOP_GAME_BUTTON_TEXT);
   button_obj.Selectable(false);
   control_panel_obj_arr.Add(button_obj);
   //moving the control panel
   ControlPanelMoveOnChart();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Control Panel movement method                                    |
//+------------------------------------------------------------------+
void CSnakeGame::ControlPanelMoveOnChart()
  {
   CChartObjectButton *button_obj;
   int x,y,i;
   x=header_left;
   y=header_top+HEADER_HEIGHT+COUNT_ROWS*(SQUARE_HEIGHT-1)+1;
   i=0;
   while((button_obj=control_panel_obj_arr.At(i))!=NULL)
     {
      button_obj.X_Distance(x+i*CONTROL_WIDTH);
      button_obj.Y_Distance(y+CONTROL_HEIGHT);
      i++;
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Control Panel deletion method                                    |
//+------------------------------------------------------------------+
void CSnakeGame::DeleteControlPanel()
  {
   delete control_panel_obj_arr;
   ChartRedraw();
  }

Oyun Başlatma, Başlatmama ve Oyun Öğelerinin Hareketi

//+------------------------------------------------------------------+
//| Game elements movement method                                    |
//+------------------------------------------------------------------+
void  CSnakeGame::AllMoveOnChart()
  {
   FieldMoveOnChart();
   StatusPanelMoveOnChart();
   ControlPanelMoveOnChart();
   ObstacleMoveOnChart();
   SnakeMoveOnChart();
   FoodMoveOnChart();
  }
//+------------------------------------------------------------------+
//| Game initialization                                              |
//+------------------------------------------------------------------+
void CSnakeGame::Init()
  {
   CreateHeader();
   CreateField();
   CreateStatusPanel();
   CreateControlPanel();
   CreateObstacle();
   CreateSnake();
   CreateFood();
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Game deinitialization                                            |
//+------------------------------------------------------------------+
void  CSnakeGame::Deinit()
  {
   DeleteFood();
   DeleteSnake();
   DeleteObstacle();
   DeleteControlPanel();
   DeleteStatusPanel();
   DeleteField();
   DeleteHeader();
  }

Oyun Kontrolü

//+------------------------------------------------------------------+
//| Dummy Start game method                                          |
//+------------------------------------------------------------------+
void CSnakeGame::StartGame()
  {
   return;
  }
//+------------------------------------------------------------------+
//| Dummy Pause game method                                          |
//+------------------------------------------------------------------+
void CSnakeGame::PauseGame()
  {
   return;
  }
//+------------------------------------------------------------------+
//| Stop game method                                                 |
//+------------------------------------------------------------------+
void CSnakeGame::StopGame()
  {
   CChartObjectEdit *edit_obj;
   current_level=0;
   current_lives=LIVES_SNAKE;
   //setting new value for the "Level" field of the status panel
   edit_obj=status_panel_obj_arr.At(0);
   edit_obj.Description(StringFormat(spaces6+LEVEL_EDIT_TEXT,current_level,MAX_LEVEL));
   //setting new value for the "Lives" field of the status panel
   edit_obj=status_panel_obj_arr.At(2);
   edit_obj.Description(StringFormat(spaces8+LIVES_EDIT_TEXT,current_lives));
   DeleteFood();
   DeleteSnake();
   DeleteObstacle();
   CreateObstacle();
   CreateSnake();
   CreateFood();
  }
//+------------------------------------------------------------------+
//| Level restart method                                             |
//+------------------------------------------------------------------+
void CSnakeGame::ResetGame()
  {
   CChartObjectEdit *edit_obj;
   if(current_lives-1==-1)StopGame();
   else
     {
      //decreasing the number of lives
      current_lives--;
      //updating it at the status panel
      edit_obj=status_panel_obj_arr.At(2);
      edit_obj.Description(StringFormat(spaces8+LIVES_EDIT_TEXT,current_lives));
      DeleteFood();
      DeleteSnake();
      CreateSnake();
      CreateFood();
     }
  }
//+------------------------------------------------------------------+
//| Next level method                                                |
//+------------------------------------------------------------------+
void CSnakeGame::NextLevel()
  {
   CChartObjectEdit *edit_obj;
   current_lives=LIVES_SNAKE;
   //to the initial level if there isn't next level
   if(current_level+1>MAX_LEVEL)StopGame();
   else
     {
      //else increasing the level and updating the startus panel contents
      current_level++;
      edit_obj=status_panel_obj_arr.At(0);
      edit_obj.Description(StringFormat(spaces6+LEVEL_EDIT_TEXT,current_level,MAX_LEVEL));
      edit_obj=status_panel_obj_arr.At(2);
      edit_obj.Description(StringFormat(spaces8+LIVES_EDIT_TEXT,current_lives));
      DeleteFood();
      DeleteSnake();
      DeleteObstacle();
      CreateObstacle();
      CreateSnake();
      CreateFood();
     }
  }

Olay İşleme (son kod)

// Declaring and creating an object of CSnakeGame type at global level
CSnakeGame snake_field;    
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   snake_field.Init();
   EventSetTimer(1);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   snake_field.Deinit();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTimer()
  {
   //setting the buttons unpressed
   if(ObjectFind(0,START_GAME_BUTTON_NAME)>=0 && ObjectGetInteger(0,START_GAME_BUTTON_NAME,OBJPROP_STATE)==true)
      ObjectSetInteger(0,START_GAME_BUTTON_NAME,OBJPROP_STATE,false);
   if(ObjectFind(0,PAUSE_GAME_BUTTON_NAME)>=0 && ObjectGetInteger(0,PAUSE_GAME_BUTTON_NAME,OBJPROP_STATE)==true)
      ObjectSetInteger(0,PAUSE_GAME_BUTTON_NAME,OBJPROP_STATE,false);
   if(ObjectFind(0,STOP_GAME_BUTTON_NAME)>=0 && ObjectGetInteger(0,STOP_GAME_BUTTON_NAME,OBJPROP_STATE)==true)
      ObjectSetInteger(0,STOP_GAME_BUTTON_NAME,OBJPROP_STATE,false);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   long x,y;
   static bool press_key=true;
   static bool press_button=false;
   static bool move=false;
   //if key has been pressed and the snake has moved, let's specify the new movement direction
   if(id==CHARTEVENT_KEYDOWN && press_key==false)
     {
      if((lparam==VK_LEFT) && (snake_field.GetDirection()!=DIRECTION_LEFT && snake_field.GetDirection()!=DIRECTION_RIGHT))
         snake_field.SetDirection(DIRECTION_LEFT);
      else if((lparam==VK_RIGHT) && (snake_field.GetDirection()!=DIRECTION_LEFT && snake_field.GetDirection()!=DIRECTION_RIGHT))
         snake_field.SetDirection(DIRECTION_RIGHT);
      else if((lparam==VK_DOWN) && (snake_field.GetDirection()!=DIRECTION_UP && snake_field.GetDirection()!=DIRECTION_DOWN))
         snake_field.SetDirection(DIRECTION_DOWN);
      else if((lparam==VK_UP) && (snake_field.GetDirection()!=DIRECTION_UP && snake_field.GetDirection()!=DIRECTION_DOWN))
         snake_field.SetDirection(DIRECTION_UP);
      press_key=true;
     }
   //if "Start" button has been pressed and press_button=false
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==START_GAME_BUTTON_NAME && press_button==false)
     {
         //waiting 1 second
         Sleep(1000);
         //generating new event for snake movement
         EventChartCustom(0,0,0,0,"");
         press_button=true;
     }
   //if "Pause" button has been pressed
   else if(id==CHARTEVENT_OBJECT_CLICK && sparam==PAUSE_GAME_BUTTON_NAME)
     {
      press_button=false;
     }
   //if "Stop" button has been pressed
   else if(id==CHARTEVENT_OBJECT_CLICK && sparam==STOP_GAME_BUTTON_NAME)
     {
      snake_field.StopGame();
      press_key=true;
      press_button=false;
     }
   //processing of the snake movement event, if press_button=true
   else if(id==CHARTEVENT_CUSTOM && press_button==true)
     {
      snake_field.SnakeMoveOnField();
      press_key=false;
     }
   //processing of the game restart event
   else if(id==CHARTEVENT_CUSTOM+1)
     {
      snake_field.ResetGame();
      Sleep(1000);
      press_key=true;
      press_button=false;
     }
   //processing of the next level event
   else if(id==CHARTEVENT_CUSTOM+2)
     {
      snake_field.NextLevel();
      Sleep(1000);
      press_key=true;
      press_button=false;
     }
   //processing of the header movement event
   else if(id==CHARTEVENT_OBJECT_DRAG && sparam==HEADER_BUTTON_NAME)
     {
      x=ObjectGetInteger(0,sparam,OBJPROP_XDISTANCE);
      y=ObjectGetInteger(0,sparam,OBJPROP_YDISTANCE);
      snake_field.SetHeaderPos(x,y);
      snake_field.AllMoveOnChart();
     }
  }
//+------------------------------------------------------------------+

press_key ve press_button, OnChartEvent olay işleyici işlevinde tanımlanan iki statik değişkendir.

press_button değişkeni false ise oyunun başlamasına izin verilir. "Başlat" düğmesine tıklandıktan sonra, press_button değişkeni true olarak ayarlanır (oyunu başlatan kodun yeniden yürütülmesini yasaklar), bu durum aşağıdaki olaylardan birine kadar aynı kalır:

  • Mevcut seviyenin yeniden başlatılması,
  • Bir sonraki seviyeye geçiş,
  • Oyunu duraklatma ("Duraklat" düğmesine basılmıştır),
  • Oyunu dururma ("Durdur" düğmesine basılmıştır).

Yılanın hareket yönünün değiştirilmesi, mevcut yöne dik doğrultuda olması ve ayrıca yılan oyun alanında hareket ettikten sonra mümkündür (press_key değişkeninin değeri bunu gösterir) Bu koşullar, CHARTEVENT_KEYDOWN olay işleme işlevinde (keypress olayı) dikkate alınır.

Ardından üstbilgiyi taşırsanız CHARTEVENT_OBJECT_DRAG olayı oluşturulur, CSnakeGame sınıfının header_left ve header_top alanları yeniden tanımlanır. Diğer oyun öğelerinin hareketi bu alanların değerleriyle belirlenir.

Oyun alanının hareketi, TradePad_Sample'da sunulan şekilde uygulanır.

Sonuç

Bu makalede oyunların MQL5'te yazılmasına ilişkin bir örneği ele aldık.

Standart Kitaplık sınıflarını (kontrol sınıfları), CArrayObj sınıfını tanıttık ve ayrıca olay işleme ile periyodik işlev çağrısının nasıl gerçekleştirileceğini öğrendik.

"Yılan" oyununun kaynak kodlarını içeren bir arşiv aşağıdaki referanstan indirilebilir. Arşiv, terminal_data_folder klasörüne açılmalıdır.

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/65

Ekli dosyalar |
snake-mql5-doc.zip (405.1 KB)
Göstergeleri MQL4'ten MQL5'e Aktarma Göstergeleri MQL4'ten MQL5'e Aktarma
Bu makale, MQL4'te yazılan fiyat yapılarının MQL5'e aktarılmasına ilişkin özelliklere ayrılmıştır. Gösterge hesaplamalarını MQL4'ten MQL5'e aktarma işlemini kolaylaştırmak için mql4_2_mql5.mqh işlevler kitaplığı önerilir. Kullanımı MACD, Stokastik ve RSI göstergelerinin aktarımı temelinde açıklanmıştır.
Expert Advisor'ın MQL5'te ICQ ile Bağlantısı Expert Advisor'ın MQL5'te ICQ ile Bağlantısı
Bu makalede, Expert Advisor ve ICQ kullanıcıları arasındaki bilgi alışverişi yöntemi açıklanmakta olup birkaç örnek sunulmaktadır. Sunulan materyal, bir istemci terminalinden, bir ICQ istemcisi aracılığıyla cep telefonlarına veya PDA'larına alım satım bilgilerini uzaktan almak isteyen kişiler için ilgi çekici olacaktır.
Piyasa Analizi için Veritabanlarının Pratik Uygulaması Piyasa Analizi için Veritabanlarının Pratik Uygulaması
Verilerle çalışmak, hem bağımsız hem de ağ uygulamaları için modern yazılımların ana görevi haline geldi. Bu sorunu çözmek için özel bir yazılım oluşturuldu. Bunlar, bilgisayar depolama ve işlemeleri için verileri yapılandırabilen, sistematikleştirebilen ve düzenleyebilen Veritabanı Yönetim Sistemleridir (DBMS). Alım satım işlemine gelince, analistlerin çoğu çalışmalarında veritabanlarını kullanmaz. Ancak böyle bir çözümün işe yaraması gereken görevler vardır. Bu makale, hem istemci-sunucu hem de dosya-sunucu mimarileriyle veritabanlarından veri kaydedebilen ve yükleyebilen göstergelere ilişkin bir örnek sağlar.
Alım Satım için MQL5'te Aktif Kontrol Panelleri Oluşturma Alım Satım için MQL5'te Aktif Kontrol Panelleri Oluşturma
Makalede, MQL5'te aktif kontrol panellerinin geliştirilmesi sorunu ele alınmaktadır. Arayüz öğeleri, olay işleme mekanizması tarafından yönetilir. Ayrıca, kontrol öğeleri özelliklerinin esnek bir şekilde ayarlanması seçeneği de mevcuttur. Aktif kontrol paneli, pozisyonlarla çalışılmasına ve piyasa ve bekleyen talimatların ayarlanmasına, değiştirilmesine ve silinmesine izin verir.