Skip to content

Book-Esenthel-Basics

I started with the Basic section from the mutecode book for Esenthel. Here is the template of an Esenthel application
Created date: 2022-02-22

1 Introduction

/******************************************************************************/
void InitPre() // initialize before engine inits
{
   INIT(); // call auto-generated function that will setup application name, load engine and project data
}
/******************************************************************************/
bool Init() // initialize after engine is ready
{
   // here when engine will be ready you can load your game data
   // return false when error occurred
   return true;
}
/******************************************************************************/
void Shut() // shut down at exit
{
   // this is called when the engine is about to exit
}
/******************************************************************************/
bool Update() // main updating
{
   // here you have to process each frame update

   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed
   return true;                   // continue
}
/******************************************************************************/
void Draw() // main drawing
{
   // here you have to tell engine what to draw on the screen

   D.clear(AZURE); // clear screen to azure color
   D.text (0, 0, "Hello to " ENGINE_NAME " Engine !"); // display text at (0, 0) screen coordinates
}
/******************************************************************************/

2 Random

Để có một game thú vị thì cần phải có các events ngẫu nhiên, vì vậy sử dụng Random function sẽ rất thường xuyên trong các games.

EE có class function là Random() để cho ra các số nguyên và Random.f() để ra số thực

Trong Update() function

   if(Kb.bp(KB_SPACE))
   {
      number = Random(30);
      myColor.set(Random(256), Random(256), Random(256));
      myCircle.r = Random.f(); // Random radius size của Circle
      myCircle.pos.x = Random.f(-D.w(), D.w()); // Random vị trí của Circle từ bên trái sang phải của màn hình (-1,1)
      myCircle.pos.y = Random.f(-D.h(), D.h());
   }

Kết quả application đầu tiên với Random một circle và background theo màu sắc như sau (sorry vì màu hơi nhức đầu)

3 Interaction with mouse and keyboard

3.1 Mouse position trong class Ms()

Trong Update() function có thể update vị trí con trỏ chuột và trong void Draw() thì cho phép Draw nó lên

bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   mousePos = Ms.pos();
   return true;                   // continue
}
void Draw() // main drawing
{
   // here you have to tell engine what to draw on the screen

   D.clear(BLACK); // Random background color
   mousePos.draw(RED);
}

Kết quả như sau:

Ngoài ra cũng có thể giữ khoảng cách giữa con trỏ và Red point

bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   mousePos = Ms.pos();
   //mousePos.x += 0.1;
   mousePos.y += 0.1;
   return true;                   // continue
}

3.2 Mouse clicks

Mouse thường có chuột trái, phải và con lăn, ngoài ra cũng có ấn vào chuột giữa hoặc con lăn để rotate.

Bây giờ chỉ hiển thị trỏ chuột khi ấn chuột trái (pushed), nếu bỏ ra (released) thì sẽ biến mất. Dùng màu BLUE để phân biệt

// Tạo các global variable
Vec2 mousePos;
Vec2 mousePos2;
Str myString = "Push left mouse to drag BLUE pointer";

/******************************************************************************/
// Update Game: Các animation trong game cần liên tục update
bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   mousePos = Ms.pos();
   //mousePos.x += 0.1;
   mousePos.y += 0.1;

   if (Ms.b(0)) // nếu button, press/pushed=True, chuột trái (0), (1) là chuột phải 
   {                                   
      mousePos2 = Ms.pos();
      myString = "Left mouse turned ON";
   } else if (Ms.br(0)) // br tức là button release, khi thả chuột trái ra
   {
      myString = "Left mouse turned OFF";
   }    


   return true;                   // continue
}
/******************************************************************************/
void Draw() // main drawing
{
   // here you have to tell engine what to draw on the screen

   D.clear(BLACK); // Random background color
   mousePos.draw(RED);
   mousePos2.draw(BLUE);
   D.text(0.0, 0.0, myString);
}

Ngoài ra còn có các Ms.b*() khác như

  • Ms.d là double click,
  • Ms.bp là button press trong 1 frame. Nếu sử dụng function này thì pointer chỉ cập nhật vị trí mới tại thời điểm click chuột chứ không kéo đi được đến vị trí khác dù cho có không thả chuột. Ngược lại thì Ms.b sẽ cập nhật liên tục nếu không thả chuột.
  • Còn có nhiều Ms khác được built sẵn trong Esenthel

3.3 Mouse wheel

Sử dụng con lăn của chuột.

Khi sử dụng Ms.wheel sẽ tạo ra một giá trị delta (có thể positive và negative) để so sánh distance giữa các update

Thử thách:
Khi ấn chuột phải thì pointer sẽ cố định lại.
Sử dụng con lăn để di chuyển chuột theo hàng ngang.

bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed


   if (Ms.b(0)) // nếu button, press/pushed=True, chuột trái (0), (1) là chuột phải 
   {                                   
      mousePos2 = Ms.pos();
      myString = "Left mouse turned ON";
   } else if (Ms.br(0)) // br tức là button release, khi thả chuột trái ra
   {
      myString = "Left mouse turned OFF";
   }    

   if (Ms.b(1))
   {
      mousePos.y +=Ms.wheel() * 0.02; // 0.02 tức là 2% distance 
   }
   return true;                   // continue
}

Lưu ý khi này cần phải bỏ mousePos=Ms.pos() trong Update() nếu không con trỏ sẽ liên tục trở lại vị trí cũ dù cho có sử dụng con lăn.

Change cursor image

3.4 Keyboard

Trong các game thường sử dụng phím WASD để di chuyển nhân vật.
Keyboard được sử dụng tương tự như Ms.b*(), chỉ khác là Kb.b*() các phím sử dụng như KB_SPACE

Cần chú ý đến bàn phím tiếng anh QWERTY hay bàn phím tiếng Pháp AZERTY
Hy vọng Esenthel có khả năng tự phân biết. Hoặc chúng ta phải code 1 chút để game tự phân biệt được.

Challenge: Tạo 1 Circle di chuyển trên màn hình. Khi ấn phím WASD thì nó sẽ di chuyển 0.05 units màn hình.
Nếu ấn thêm phím Spacebar thì speed của nó sẽ được boost lên, di chuyển với tốc độ 0.1 units và Circle sẽ chuyển từ BLUE sang RED

Vì có 4 phím nên sử dụng switch, nó tương đương với nhiều if. Cấu trúc như sau:

switch(expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
    // code block
}

Tuy nhiên bây giờ mình sẽ sử dụng if trước cho đơn giản.

bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   Ms.cursor(UID(3653639881, 1089848782, 3260974724, 1101079839)); // Change cursor image

   myCircle.r = 0.02;
   if (Kb.b(KB_W)) myCircle.pos.y += 0.05;
   if (Kb.b(KB_S)) myCircle.pos.y -= 0.05;
   if (Kb.b(KB_A)) myCircle.pos.x -= 0.05;
   if (Kb.b(KB_D)) myCircle.pos.x += 0.05;

   // Khi Circle chạm phải viền màn hình thì sẽ đi về vị trí ban đầu
   // Sử dụng Display width or Display height của màn hình
   if (myCircle.pos.x > D.w()) myCircle.pos.x= -D.w();
   if (myCircle.pos.x < -D.w()) myCircle.pos.x= D.w();
   if (myCircle.pos.y > D.h()) myCircle.pos.y= -D.h();
   if (myCircle.pos.y < -D.h()) myCircle.pos.y= D.h();
   return true;                   // continue
}
/******************************************************************************/
void Draw() // main drawing
{
   // here you have to tell engine what to draw on the screen

   D.clear(BLACK); // Random background color
   myCircle.draw(BLUE);
}

Bây giờ sẽ add thêm chế độ Super Saiya khi sử dụng spacebar. Chỉ cần thêm biến speedmyColor để có thể thay đổi khi sử dụng phím SpaceBar

Circle myCircle;

Color myColor;
int speed;
/******************************************************************************/
// Update Game: Các animation trong game cần liên tục update
bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   Ms.cursor(UID(3653639881, 1089848782, 3260974724, 1101079839)); // Change cursor image

   myCircle.r = 0.02;


   speed = (Kb.b(KB_SPACE)) ? 2 : 1; // Sử dụng ternary expression cho nhanh
   myColor = (Kb.b(KB_SPACE)) ? RED : BLUE;

   if (Kb.b(KB_W)) myCircle.pos.y += 0.05*speed;
   if (Kb.b(KB_S)) myCircle.pos.y -= 0.05*speed;
   if (Kb.b(KB_A)) myCircle.pos.x -= 0.05*speed;
   if (Kb.b(KB_D)) myCircle.pos.x += 0.05*speed;

3.5 Delta time in Game

Time.d() sẽ giúp cho game hoạt động tương đồng trên các máy tính khác nhau. Vì có máy là 60FPS có máy là 30FPS. Time.d() sẽ cho kết quả của unit per second.

Bài tập di chuyển con trỏ sẽ được thay vì dùng 0.05*speed sẽ dùng Time.d()*speed

   if (Kb.b(KB_W)) myCircle.pos.y += Time.d()*speed;
   if (Kb.b(KB_S)) myCircle.pos.y -= Time.d()*speed;
   if (Kb.b(KB_A)) myCircle.pos.x -= Time.d()*speed;
   if (Kb.b(KB_D)) myCircle.pos.x += Time.d()*speed;

Kết quả animation sẽ smooth hơn so với dùng 0.05.


4 Shapes

Esenthel có các shape có sẵn như Circle, Edge, Rectangle

Để vẽ 1 đường line thì cần dùng Edge2, 2 tức là trong môi trường 2D.

Challenge: Tạo một vòng tròn, có 1 hoặc 2 lines di chuyển như đồng hồ

  • Vẽ Circle
  • Vẽ Line là Second
  • Vẽ Line là Minute
  • Vẽ Line là Hour
  • Sử dụng Time.curTime() để lấy được thời điểm theo thời gian thực.
  • Một Circle là 180 độ, tương đương với 60 giây –> 3 độ/giây –> Một delta time sẽ di chuyển 3 độ.
  • Lưu ý SinCos trong Esenthel sử dụng Radian chứ không phải degree. Tức là Sin(30 độ) phải ghi là Sin(PI/6)

Hiện tại mình chỉ vẽ cho second cho đơn giản.

Edge2 secondLine;
int speed=1;
float x=0.0;
float y=0.5;
float beta;
float clockRadius=0.5;
Circle clockBound;
TextStyleParams ts;

/******************************************************************************/
// Update Game: Các animation trong game cần liên tục update
bool Update() // main updating
{
   if(Kb.b(KB_ESC))return false; // exit if escape on the keyboard pressed

   clockBound.r = clockRadius; // Tạo vòng tròn với bán kính là 0.5

   beta =(PI/2-speed*Time.curTime()*2*PI/60);
   x = Cos(beta)*clockRadius;
   y = Sin(beta)*clockRadius;
   // Update vị trí của kim giây.
   secondLine.set(Vec2(0, 0), Vec2(x, y));

   speed=(Kb.b(KB_SPACE)) ? 10 : 1;
   ts.color=YELLOW; // Khai báo màu cho Clock TExt
   return true;                   // continue
}
/******************************************************************************/
void Draw() // main drawing
{
   // here you have to tell engine what to draw on the screen

   D.clear(BLACK); // Random background color
   D.text(0, 0.7, S+"Degree: " + beta*180/PI);
   D.text(0, 0.8, S+"Running time (second): "+ Time.curTime());
   clockBound.draw(WHITE);
   secondLine.draw(RED, 0.005); // Color then width
   D.text(ts, 0, 0.55, "12");
   D.text(ts,0, -0.55, "6");
   D.text(ts,0.55, 0, "3");
   D.text(ts,-0.55, 0, "9");

}

Kết quả tạo đồng hồ với kim giây. Sử dụng phím SPACEBAR để boost tốc độ lên 10 lần cho dễ kiểm tra.

5 Cuts, collision

Cuts là một functon rất có ích trong game, xác định 2 vật thể có chạm nhau hay không. Bây giờ sẽ tạo 1 game nhỏ để áp dụng chức năng này

Challenge: Cho Random các Enemy có hình dạng là Rectangle RED xuất hiện trên màn hình. Dùng BLUE Circle để thu thập các Enemy, điểm sẽ được tăng dần
1. Sử dụng code từ bài Keyboard Book-Esenthel-Basics.
2. Tạo Random các Shape Rect (hiện tại chỉ 1 enemy) trên màn hình
3. Biến mất Shape Rect khi Circle chạm vào.

Random các shape. Esenthel-Book-Basics

Đoạn code dưới đây tạo Rect có x từ 0.1 đến 0.2 và y từ -0.2 đến 0.2

enemyRect.setX(0.1, 0.2);
enemyRect.setY(-0.2, 0.2);

Để Random position của Rect thì sử dụng Random.f từ Display trái sang phải, trên xuống dưới.

// Enemy Rectangle
   float randomXpos = Random.f(-D.w(), D.w());
   float randomYpos = Random.f(-D.h(), D.h());
   float randomSize = Random.f(0.1, 0.3);
   enemyRect.setX(randomXpos, randomXpos+randomSize);
   enemyRect.setY(randomYpos, randomYpos+randomSize);

Tuy nhiên Random Rect như vậy có nhược điểm là các RECT xuất hiện quá nhanh, và không kiểm soát được

Bây giờ chỉ Tạo Rect mới khi Circle chạm vào nó. Sử dụng if Circle Cuts Rect thì tạo Rect mới.

if (Cuts(myCircle, enemyRect))
   {
      randomXpos = Random.f(-D.w(), D.w());
      randomYpos = Random.f(-D.h(), D.h());
      randomSize = Random.f(0.1, 0.3);
      enemyRect.setX(randomXpos, randomXpos+randomSize);
      enemyRect.setY(randomYpos, randomYpos+randomSize);
   }

Sản phẩm:

Để tăng độ khó thì Enemy cần có khả năng di chuyển. Luật chơi là Enemy chỉ di chuyển khi Circle lại gần 20%, nó có tốc độ đi khoảng 80 - 120% so với Circle. Nó có khả năng đi trở lại màn hình như Circle

// Tạo các global variable
Circle myCircle;
Rect enemyRect;

Color myColor;
int speed;
float randomXpos=0.1;
float randomYpos=0.1;
float randomSize=0.1;
/******************************************************************************/
// Update Game: Các animation trong game cần liên tục update
bool Update() // main updating
{
   if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed

   // My Circle position
   myCircle.r = 0.02;
   speed = (Kb.b(KB_SPACE)) ? 2 : 1; // Sử dụng ternary expression cho nhanh
   myColor = (Kb.b(KB_SPACE)) ? YELLOW : BLUE;

   if (Kb.b(KB_W))
   {
      myCircle.pos.y += Time.d()*speed; // Di chuyển Circle dựa theo WASD
      // Nếu Cicle gần Enemy 20% thì Enemy sẽ di chuyển giống như bàn phím với tốc độ 0.8 đến 1.2
      if (Abs(myCircle.pos.y - enemyRect.centerY())<0.2) enemyRect.moveY(Time.d()*Random.f(0.8, 1.2));
   } 
   if (Kb.b(KB_S))
   {
      myCircle.pos.y -= Time.d()*speed;
      if (Abs(myCircle.pos.y - enemyRect.centerY())<0.2) enemyRect.moveY(-Time.d()*Random.f(0.8, 1.2));
   }
   if (Kb.b(KB_A))
   {
      myCircle.pos.x -= Time.d()*speed;
      if (Abs(myCircle.pos.x - enemyRect.centerX())<0.2) enemyRect.moveX(-Time.d()*Random.f(0.8, 1.2));
   }
   if (Kb.b(KB_D))
   {
      myCircle.pos.x += Time.d()*speed;
      if (Abs(myCircle.pos.x - enemyRect.centerX())<0.2) enemyRect.moveX(Time.d()*Random.f(0.8, 1.2));
   }

   // Khi Circle chạm phải viền màn hình thì sẽ đi về vị trí ban đầu
   // Sử dụng Display width or Display height của màn hình
   if (myCircle.pos.x > D.w()) myCircle.pos.x= -D.w();
   if (myCircle.pos.x < -D.w()) myCircle.pos.x= D.w();
   if (myCircle.pos.y > D.h()) myCircle.pos.y= -D.h();
   if (myCircle.pos.y < -D.h()) myCircle.pos.y= D.h();

   // Không cho Rect đi xuyên màn hình
   if (enemyRect.centerX() >  D.w()) enemyRect.setX(-D.w(), -D.w()+randomSize);
   if (enemyRect.centerX() < -D.w()) enemyRect.setX( D.w(), D.w()+randomSize);
   if (enemyRect.centerY() >  D.h()) enemyRect.setY(-D.h(), -D.h()+randomSize);
   if (enemyRect.centerY() < -D.h()) enemyRect.setY( D.h(), D.h()+randomSize);



   // Create Enemy Rectangle when Circle collide
   if (Cuts(myCircle, enemyRect)) // Khi Circle chạm vào Rect sẽ tạo 1 Rect tại vị trí khác
   {
      randomXpos = Random.f(-D.w(), D.w());
      randomYpos = Random.f(-D.h(), D.h());
      randomSize = Random.f(0.1, 0.3);
      enemyRect.setX(randomXpos, randomXpos+randomSize);
      enemyRect.setY(randomYpos, randomYpos+randomSize);
   }


   return true;                   // continue
}

6 Next

Now I will go to the chapter two of this book in the other note, because this note is already long.

The following pages link to this page:



Created : Feb 22, 2022