MQL5'te "Yılan" Oyunu Oluşturma
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:
- 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.
- 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.
- 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.
- 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:
- Yöne bağlı olarak hücre kafası sağa, sola, yukarı veya aşağı hareket eder.
- Yılanın gövdesinin son öğesinin bir önceki kafa yerindeki hareketi.
- 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:
- CArrayObj, veri düzenlemesi (dinamik işaretçiler dizisi) için sınıftır.
- 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
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz