Book-Esenthel-2D-Basics
Khi game có rất nhiều đối tượng cùng dạng dữ liệu mà khó quản lý như nhiều enemy thì chúng ta có thể dùng container
. Trong Esenthel
có function Memc
để khai báo container
.
Created date: 2022-02-27
1 Containers-Esenthel¶
Cú pháp khai báo như sau:
Sử dụng
elms()
để đếm số variables có trong container
Bây giờ sẽ thử tạo 10 circles có vị trí random trên màn hình
// Tạo các global variable
Memc<Circle> myCircles;
/******************************************************************************/
bool Init() // initialize after engine is ready
{
// Tại function Init() này có thể khai báo dữ liệu ban đầu của các variables
// Tạo 10 circles cho container myCircles
for(int i=0; i<10; i++)
{
// SỬ dụng function class New() để add variables mới vài vị trí cuối của container
myCircles.New().set(Random.f(0.05, 0.15), // function set thì có từ Circle
Vec2(Random.f(-D.w(), D.w()), Random.f(-D.h(), D.h())));
}
return true;
}
/******************************************************************************/
void Shut() // shut down at exit
{
}
/******************************************************************************/
// 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
return true; // continue
}
/******************************************************************************/
void Draw() // main drawing
{
D.clear(BLACK); // Random background color
for(int i=0; i<myCircles.elms(); i++)
{
myCircles[i].draw(RED);
}
}
Để các Circle này xuất hiện lần lượt thì cần sử dụng thêm 1 hàm if
void Shut() // shut down at exit
{
}
/******************************************************************************/
// 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
if(Kb.bp(KB_SPACE))
{
myCircles.New().set(Random.f(0.05, 0.15), // function set thì có từ Circle
Vec2(Random.f(-D.w(), D.w()), Random.f(-D.h(), D.h())));
}
return true; // continue
}
/******************************************************************************/
void Draw() // main drawing
{
D.clear(BLACK); // Random background color
for(int i=0; i<5; i++)
{
myCircles[i].draw(RED);
}
for(int i=0; i<myCircles.elms()-5; i++)
{
myCircles[i].draw(BLUE);
}
}
Ngoài function New()
thì cũng có remove()
. Function này sẽ rất có ích trong trường hợp có collision
, các enemy trong container
sẽ biến mất nếu va chạm với nhân vật (hoặc bị tiêu diệt) . Nếu muốn xoá hết thì dùng clear()
Challenge: Tạo game đặt bom, nhân vật là Circle BLUE, có khả năng thải ra các quả bom là các Rect BLUE. Enemy là các Circle RED xuất hiện random trên màn hình mỗi 2 giây. Các quả bơm có khả năng nổ dọc màn hình.
Cần di chuyển Circle BLUE để nổ hết các Circle RED
Hướng giải quyết: 1 Tạo myCircle là nhân vật trong game, có khả năng di chuyển. 2 Dùng Memc<Rect>
để tạo các myRectBoom. Ấn Space sẽ tạo ra quả Boom. 3 Dùng Memc<Circle>
để tạo nhiều enemyCircles, khi nhân vật chạm vào Enemy sẽ Game Over.
bool Update() // main updating
{
if(Kb.bp(KB_ESC))return false; // exit if escape on the keyboard pressed
myCircle.r = 0.08; // kích thước Circle
if (Kb.b(KB_W)) myCircle.pos.y += Time.d(); // Di chuyển Circle dựa theo WASD
if (Kb.b(KB_S)) myCircle.pos.y -= Time.d();
if (Kb.b(KB_A)) myCircle.pos.x -= Time.d();
if (Kb.b(KB_D)) myCircle.pos.x += Time.d();
// Khi Circle chạm phải viền màn hình thì sẽ đi về vị trí ban đầu
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();
if (Kb.b(KB_SPACE)) // Thải boom bằng phím Space
{
myBoom.set(myCircle.pos.x-0.07, -D.h(), myCircle.pos.x+0.07, D.h());
}
for(int i=0; i<enemyCircles.elms(); i++)
{
if(Cuts(enemyCircles[i], myBoom)) enemyCircles.remove(i);
}
return true; // continue
}
/******************************************************************************/
void Draw() // main drawing
{
D.clear(BLACK); // Random background color
for(int i=0; i<enemyCircles.elms(); i++) // vẽ Enemy Red
{
enemyCircles[i].draw(RED);
}
myCircle.draw(BLUE); // Draw character
myBoom.draw(YELLOW, false);
D.text(-1, 0.95, S+"My Circle Position: "+myCircle.pos);
}
/******************************************************************************/
Kết quả như sau
Còn một số nội dung như Enemy xuất hiện theo thời gian thì practice ở các bài khác, vì tôi đang rất phấn khởi với các kiến thức mới.
2 Class-Esenthe¶
Khi các objects hoặc variables cung cấp thông tin đến cùng một nội dung như HP, mana, skill đều là variables của Player
thì tạo class player
với các functions
là HP, mana...
2.1 Functions of class¶
Vì trong các functions có sẵn của66 Esenthel hay standard library của C++ không có các chức năng cụ thể mà chúng ta cần, vì vậy tuỳ trường hợp mà tạo các functions cho các class khác nhau. Tiếp tục trong trường hợp của game đặt boom, các enemy RED
chỉ xuất hiện random và đứng im tại 1 vị trí. Để cải thiện game này thì enemy
cần phải có khả năng di chuyển ngang màn hình vói tốc độ Random.
Bây giờ hãy tạo một class movingCircle
có các function như reset position, moving along x axis. Sau đó dùng Container Memc
dể tạo các enemy theo class
vừa được tạo (nó sẽ tương đương với Memc<Circle> names
nhưng sẽ có thể áp dụng được nhiều trường hợp khác)
2.2 Gán dữ liệu từ argument của function sang local variables¶
Ví dụ trong handmade class movingCircle
có tạo các local variables như speed
color
là các variables có thuộc tính từ class có sẵn của Esenthel
. Trong void create() function
có chứa các arguments
như speed
và color
, sau đó sử dụng this
hoặc T.
để gán dữ liệu từ argument
sang local variables
và sử dụng được cho các class function
khác trong class movingCircle
. Nếu không dùng this
thì các variables này không thể truy cập được trong các class khác. Chính vì vậy trong C++, recommend sử dụng cùng tên của local variables
và arguments variables
class movingCircle
{
Circle shapeCircle;
Vec2 speed;
Color color;
void create(float radius, Vec2 pos, Vec2 speed, Color color)// Create function with arguments for movingCircle
{
shapeCircle.r = radius; // .r là funtion có sẵn từ class Circle
shapeCircle.pos = pos; // .pos là function có sẵn từ class Circle
T.speed = speed; // this speed is equal to speed in local variable
T.color = color; // sử dụng T. (tức là this) để gán dữ liệu từ argument sang local variables
}
Có một vấn đề là không thể sử dụng được Cuts
nữa vì trong Esenthel không có class movingCircle
, chúng ta phải tự tạo một functions Cuts khác để đánh giá. Hoặc có thể dụng pointer
cast
nhưng tôi chưa hiểu rõ các function này nên tam dùng 1 biến trung gian. Kết quả như sau
Đây là class vừa tạo
class movingCircle
{
Circle shapeCircle;
Vec2 speed; // speed là vector di chuyển theo (x,y), khi nhân với delta time sẽ cho hiệu ứng moving
Color color;
void create(float radius, Vec2 pos, Vec2 speed, Color color)// Create function with arguments for movingCircle
{
shapeCircle.r = radius; // .r là funtion có sẵn từ class Circle
shapeCircle.pos = pos; // .pos là function có sẵn từ class Circle
T.speed = speed; // this speed is equal to speed in local variable
T.color = color; // sử dụng T. (tức là this) để gán dữ liệu từ argument sang local variables
}
// Tạo function move để check
void move()
{
Vec2 newPos = shapeCircle.pos + speed * Time.d();
shapeCircle.pos = newPos;
if (shapeCircle.pos.x<-D.w()) shapeCircle.pos.x=D.w();
if (shapeCircle.pos.x> D.w()) shapeCircle.pos.x=-D.w();
if (shapeCircle.pos.y<-D.h()) shapeCircle.pos.y=D.h();
if (shapeCircle.pos.y> D.w()) shapeCircle.pos.y=-D.h();
}
// Tạo function cho return là Vec2, toạ độ của shapeCircle
Vec2 getPos()
{
return shapeCircle.pos;
}
// Function Get radius
float getR()
{
return shapeCircle.r;
}
// Tạo function setPos, không cần trả kết quả mà tự làm mọi thứ bên trong function
void setPos(Vec2 newPos)
{
shapeCircle.pos = newPos;
}
// Reset position function, các lệnh hoạt động bên trong function
void reset() // void function để reset, không cần trả kết quả
{
shapeCircle.pos=0;
}
// Function draw không cần argument, các arguments đã có trong function 'create()'
void draw()
{
shapeCircle.draw(color);
}
}
Đây là content chính của game dựa trên class movingCircle
/******************************************************************************/
// Tạo các global variable
Circle myCircle;
int speed; // tốc độ của myCircle
Memc<Rect> myRectBooms;
Rect myBoom;
Rect myBoom2;
Memc<movingCircle> enemyCircles; // sử dụng handmade class movingCircle thay vì Circle
/******************************************************************************/
bool Init() // initialize after engine is ready
{
// Tạo 3 enemy từ ban đầu
for (int i =0; i<5; i++)
{
movingCircle &enemyTmp = enemyCircles.New(); // sử dụng Reference trước, sau đó mới được gán lệnh, nếu không Random chỉ dừng lại tại 1 vị trí
enemyTmp.create(Random.f(0.05, 0.15),
Vec2(Vec2(Random.f(-D.w(), D.w()), Random.f(-D.h(), D.h()))), // Random position
Vec2(Random.f(-1, 1), Random.f(-1, 1)), // random moving direction following x and y
Color(Random(256), Random(256), Random(256))// Có thể RED, BLUE.. nhưng Random cho thú vị
);
}
return true;
}
/******************************************************************************/
void Shut() // shut down at exit
{
}
/******************************************************************************/
// 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
myCircle.r = 0.08; // kích thước Circle
if (Kb.b(KB_W)) myCircle.pos.y += Time.d(); // Di chuyển Circle dựa theo WASD
if (Kb.b(KB_S)) myCircle.pos.y -= Time.d();
if (Kb.b(KB_A)) myCircle.pos.x -= Time.d();
if (Kb.b(KB_D)) myCircle.pos.x += Time.d();
// Khi Circle chạm phải viền màn hình thì sẽ đi về vị trí ban đầu
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();
if (Kb.bp(KB_SPACE)) // Thải boom bằng phím Space, sẽ nổ thành dấu +, và tạo enemy
{
myBoom.set(myCircle.pos.x-0.07, -D.h(), myCircle.pos.x+0.07, D.h()); // Nổ trục dọc
myBoom2.set(-D.w(), myCircle.pos.y-0.07, D.w(), myCircle.pos.y+0.07); // Nổ truc ngang
// Tạo 1 quả boom nhưng cũng tạo Enemy
movingCircle &enemyTmp = enemyCircles.New(); // sử dụng Reference trước, sau đó mới được gán lệnh, nếu không Random chỉ dừng lại tại 1 vị trí
enemyTmp.create(Random.f(0.05, 0.15),
Vec2(Vec2(Random.f(-D.w(), D.w()), Random.f(-D.h(), D.h()))), // Random position
Vec2(Random.f(-1, 1), Random.f(-1, 1)), // random moving direction following x and y
Color(Random(256), Random(256), Random(256))// Có thể RED, BLUE.. nhưng Random cho thú vị
);
}
// Cho enemy di chuyển random
REPA(enemyCircles)// REPA tức là repeat all, lá một Macro của Esenthel, tương đương for i = 0 to number of Memc
{
enemyCircles[i].move();
}
Circle tempCircle; // tạo 1 Circle temporary để chuyển từ Class movingCircle thành Circle có sẵn trong Esenthel để sử dụng function Cuts.
// mặc dù điều này sẽ làm chậm game và bộ nhớ, nhưng chưa biết rõ pointer và reference cũng như cast conversion...
REPA(enemyCircles) // nếu Enemy chạm vào boom thì sẽ bị remove khỏi container. Nhược điểm là Boom k tự biến mất sau 1s. Có thể dùng container để cải thiện!
{
tempCircle.set(enemyCircles[i].getR(), enemyCircles[i].getPos());
if (Cuts(myBoom, tempCircle) || Cuts(myBoom2, tempCircle)) enemyCircles.remove(i);
}
return true; // continue
}
/******************************************************************************/
void Draw() // main drawing
{
D.clear(BLACK); // Random background color
for(int i=0; i<enemyCircles.elms(); i++) // vẽ Enemy Red
{
enemyCircles[i].draw();
}
myCircle.draw(WHITE); // Draw character
myBoom.draw(YELLOW, false);
myBoom2.draw(YELLOW, false);
D.text(-1.1, 0.95, S+"My Circle Position: "+myCircle.pos);
D.text(-1.1, 0.85, S+"Number of enemy: "+enemyCircles.elms());
}
/******************************************************************************/
Kết quả như sau:
Hiện tại game còn 1 vài nhược điểm đó là sự biến mất của quả boom sau 1s, dẫn đến tất cả enemyCircle đều biến mất khi chạm vào boom ngay khi được tạo thành.
2.3 Setters and Getters - thiết lập và tận dụng¶
Theo sách của MuteCode thì Class cần phải được thiết kế với các functions ngay từ ban đầu, dù cho có không sử dụng sau này. Hãy nghĩ về class trước, chứ không phải application.
Luật thứ 2 là không mix giữa logic
và interface GUI
network
database
trong một class.
–> Tóm lại mỗi type
của variables cần phải có một class
riêng.
Sử dụng getter
và setter
để phân biệt rõ ràng giữa class variables
và local variables
. Thông thường sẽ sử dụng _variable
để phân biệt với variable
. Ví dụ như _point
là class variable
Class trên sẽ tương đương với
class score {
int points;
int getPoints( ) { return points ; }
void setPoints(int value) { points = value; }
}
Có một lưu ý là ;
không được dùng tại cuối câu của class function
2.4 Private and public functions¶
Khi khai báo public
thì các functions
có thể được sử dụng bên ngoài class
đó, ngược lại thì private
chỉ được sử dụng trong class (không thể gọi ra được như Circle.pos.x
)
// Class scores
class score
{
private:
int points = 0;
int counter = 0;
public:
int getPoints()
{
return points;
}
score &setPoints (int points)
{
this->points = points; // sử dụng this-> tương đương với point trong private bằng points trong argument
counter++;
return T;
}
}
Bây giờ thì không thể sử dụng Score.points
nữa mà phải dùng Score.setPoints()
2.5 Class manager¶
Tương tự như các functions
của một class
, sẽ cùng thực hiện các phép tính có tính chất đóng góp cho môt đối tượng bên trong class
đó. Nhiều class
có chức năng tương tự có thể được quản lý bên trong một class
mẹ gọi là Class manager
Ví dụ như sau:
1.Tạo một class
liên quan đến Circle
- Tạo một
class manager
Trong main.cpp sẽ dùng các class
vừa tạo
Tức là trong myCircleManager
đã gọi một class
khác là myCircle
để có các chức năng. Điều này giống như chúng ta tạo một handmade class movingCircles
, nó cũng sử dụng các class
có sẵn như Circle
có từ Esenthel.
2.6 Challenge game with Class¶
- Game đặt boom như trên với một số cải thiện:
- Các enemy có speed direction random -1 đến 1. Nếu chạm boundary thì phải đi ngược lại chứ không được đi xuyên màn hình
3 Reference-Esenthel¶
Tạo các variables trung gian trong tính toán thì sẽ tốn thời gian và dung lượng cho máy tính. Chính vì vậy sử dụng reference
có thể loại bỏ được các vấn đề này.
Tóm lại, tránh các chức năng copies
các variables.
3.1 Pass by reference¶
Thay vì copy toàn bộ giá trị của variable đến variables khác
thì chỉ cần passing reference
là được.
Ví dụ như tạo 1 function
Trong trường hợp này thì Kết quả sẽ return
một value
3.2 Return by reference¶
Thay vì trả về một value
chúng ta cũng có thể trả về một reference
tức là địa chỉ của kết quả đó. Nếu value
thì máy tính vẫn cần phải tự tính toán và copy ra kết quả.
Tuy nhiên nếu return reference
trong trường hợp này sẽ bị lỗi vì pos3 = sumVec(pos1, pos2)
vẫn chưa tồn tại trong bộ nhớ, thì không thể có reference
Nguyên tắc chung của reference
là chỉ được dùng nó khi đã create it
nếu không sẽ bị lỗi.
4 Pointer¶
Pointer được tạo trước khi có reference. Nó phức tạp hơn so với reference, nhưng lại có nhiều chức năng hơn.
Trong khi reference
không thể được tạo dựa trên một empty
objects, pointer
thì có thể chỉ đến bất cứ objects nào dù cho có tồn tại hay không, và có thể thay đổi giá trị về sau.
Thông thường thì reference
được sử dụng an toàn và đơn giản hơn, dễ phát hiện các lỗi. Nhưng reference
gặp khó khăn trong return a reference
, đặt biệt là không có object được tạo (non existing object
).
Tuy nhiên pointer
cũng nên được point đến 1 reference nào đó hoặc là nullptr
nếu không application dễ bị crash
4.1 Declare pointer vs reference¶
Cách khai báo pointer
và references
như sau:
int j =43;
int& ref = j; //Khi có dấu ampersand & thì C++ sẽ đọc adress. Tuy nhiên `ref` vẫn mang giá trị của `j`
int* ptr = &j; //Khi có dấu * thì `ptr` có giá trị là adress,
ptr
không mang giá trị củaj
.- Để sử dụng giá trị của
i
thì phải dùngdereference
*ptr
- Ngoài ra
*ptr = j
cũng bị lỗi, không như reference thì vẫnvalid
vàref
có thể dùng trực tiếp nhưvalue
củaj
4.2 Changing value by pointer¶
pointer
có khả năng để thay đổi giá trị của 1 variable. Đây là một khả năng đặt biệt của pointer. Vì ý nghĩa của nó là con trỏ
, nó chỉ vào vị trí của memory
int i = 42;
int* p = &i; //ampersand & để lấy adress của i
*p = 43; // Thêm * tức là dereference để lấy giá trị thực của p
// và nó sẽ làm thay đổi giá trị của i, vì memory adress tại i bây giờ là p, nhưng nó đã bị thay đổi nhò7 dereferencing
Như đoạn code trên đã thay đổi giá trị của pointer
p
từ 42 thành 43. Tức là i
cũng thay đổi, vì i được lưu tại vị trí là p
, khi giá trị của adress đó thay đổi thì i thay đổi theo.
4.3 No existing reference vs null Pointer¶
Như thảo luận phía trên thì pointer
giúp khắc phụ lỗi của reference
khi return non existing object
. Bây giờ sẽ áp dụng lại class players
class players {
Memc<Player> list; // tạo một container tên là list có Type là Circle,
Circle& add(Vec2& pos, Str& name){//Add nhân vật vào list với 2 arguments
//đều được khai báo có sử dụng adress, bao gồm Vec2 và Str.
//Và Kết quả trả về là reference
Player& p = list.add(); // address của p thuộc dạng Player được add vào list,
// tức là khi này list[i] đã được tạo
p.set(pos, name); //Bây giờ p được gán các thuộc tính,
//tuy nhiên p không copy vào memory mà chỉ dựa theo adress của list[i]
return p; // kết quả trả về là p,
//tuy nhiên khi khai báo function là &add thì kết quả sẽ là địa chỉ
}
// Sử dụng reference vẫn ok vì class add() đã tạo một object
// Nên không bị reference đến non-existing object
Circle* findByName(Str& name){ //Dùng để tìm tên của Circle
FRPA(list){//
if(Equal(list[i].getName(),name){//
return &list[i]; // bắt buộc phải return ampersand& để lấy được adress
} // tức là findByName* phải return một adress chứ không thể là 1 value
}
}
}
Cần chú ý kết quả
return của pointer
cần phải là một adress
(sử dụng ampersand&
)
Trong file main khai báo tương tự, chỉ khác ở phần pointer
players Players;
Players.add(Vec2(1,1), "niceGuy");
player* friend = Players.findByName("coolGuy");
//Check if the player exists
if(friend != null) Greet(friend);
Khi khai báo type là pointer *friend
sử dụng variable Players
đã có list niceGuy
bên trong thì vẫn cho kết quả. Nhưng kết quả sẽ là return &list[i]
= null, vì pointer
chỉ vào non-exsisting object. Đây là khả năng của pointer
so với reference
.
4.4 Summary pointer and reference¶
Có nhiều trường hợp có thể sử dụng cả hai. Vậy thì nên sử dụng cái nào?
Lời Khuyên là sử dụng reference
bất cứ khi nào có thể, còn pointer
thì chỉ sử dụng trong trường hợp bất khả kháng.
Vì vậy chỉ sử dụng pointer
khi trong các class
mà có return là adress
thì mới cần thiết.
Một số tổng hợp từ stackoverflow và geeksforgeeks
- Pointers can be
NULL
, references cannot beNULL
. - References are easier to use,
const
can be used for a reference when we don’t want to change value and just need a reference in a function. - Pointer used with a
*
while references used with a&
. - Use pointers when pointer arithmetic operation are required.
- You can have pointers to a void type
int a=5; void *p = &a;
but cannot have a reference to a void type. - A pointer can be re-assigned
-
Reference thí không thể re-assigned value
-
A pointer has its own memory address and size on the stack whereas a reference shares the same memory address (with the original variable) but also takes up some space on the stack.
-
Indirection: You can have pointers to pointers offering extra levels of indirection. Whereas references only offer one level of indirection
-
pointer có thể khai bái như
int* i
còn reference thì không thể mà phải là reference của object nàoint& i = j
tương đương vớiint* i = &j
- Khi gán giá trị cho reference thì dùng
i = 42;
thì máy tính sẽ tự độngupdate
giá trị choj
theo. Cònpointer
thì phảidereference
*i = 42;
Tóm lại
- Use references
- In function parameters and **return types**.
- Use pointers:
- Use pointers if pointer arithmetic or passing NULL-pointer is needed. For example for arrays (Note that array access is implemented using pointer arithmetic).
- To implement data structures like linked list, tree, etc and their algorithms because to point different cell, we have to use the concept of pointers.
5 Enumeration¶
Còn được gọi tắt và enums
có chức năng liệt kê số theo string. Enums có ích khi có nhiều sự kiện cùng một type nào đó. Ví dụ như enemy
có thể là priest
, warrior
, hoặc rogue
Đọc đoạn code dưới đây để hiểu rõ hơn lợi ích của sử dụng enum
enum ENEMY_TYPE{
ET_NONE,
ET_WARRIOR,
ET_ROGUE,
ET_PRIEST,
}
class enemy{
ENEMY_TYPE type= ET_NONE; // tạo một variable là type thuộc loại ENEMY_TYPE
Rect r;
void setType(ENEMY_TYPE type) {//argument là type
T.type = type; //This type is equal to type in local variable
}
void draw(){
switch(type){
case ET_WARRIOR: Image(===drop image of warrior here===).draw(r); break;
case ET_ROGUE: Image(===drop image of rogue here===).draw(r); break;
case ET_PRIEST: Image(===drop image of priest here===).draw(r); break;
}
}
}
6 Application States¶
Hầu hết trong các games đều có các stages
khác nhau như Intro
, lobby
, in game
. Và các stages này không hoạt đồng cùng một lượt, mà lần lượt nhau hoạt động. Trong C++ có Application states
để giúp tất cả các stages này hoạt động lần lượt chứ không cùng một lượt.
Giả sử chúng ta tạo một State
tên là StateIntro
nó sẽ bao gồm các functions khác nhau như sau
State StateIntro(UpdateIntro, DrawIntro, InitIntro, ShutIntro);
bool InitIntro() {return true;}
void ShutIntro() {}
bool UpdateIntro(){
if(StateActive.time()>3 || Kb.bp(KB_ESC)){
StateMenu.set(1.0);
}
return true;
}
}
void DrawIntro(){
D.clear(BLACK);
D.text(0,0,"Intro");
}
Tương tự như vậy chúng ta sẽ có các states khác nhau như
Menu: State StateMenu(UpdateMenu, DrawMenu, InitMenu, ShutMenu);
In Game; State StateGame(UpdateGame, DrawGame, InitGame, ShutGame);
Mỗi state nên được tạo trong một file riêng. Trong file main.cpp
thì lần lượt sử dụng chúng.
void InitPre(){
INIT();
}
bool Init(){
StateIntro.set(); // state này đang được kích hoạt nên state khác chưa được dùng
return true;
}
void Shut(){}
bool Update() {return false;};
void Draw() {};
Backlinks¶
The following pages link to this page:
Created : Feb 27, 2022
Recent Posts
- 2024-11-02: BUỔI 10 - Phân tích thị trường
- 2024-11-02: BUỔI 11 - Phân tích thị trường
- 2024-11-02: BUỔI 12 - Phân tích sóng tăng
- 2024-11-02: BUỔI 13 - Phân tích hỏi đáp
- 2024-11-02: BUỔI 14 - Yếu tố kiểm soát
- 2024-11-02: BUỔI 15 - Hỏi đáp
- 2024-11-01: BUỔI 6 - Ôn lại và bổ sung
- 2024-11-01: BUỔI 7 - Chiến thuật Trend
- 2024-11-01: BUỔI 8 - Công thức điểm vào lệnh
- 2024-11-01: K2023 - BUỔI 9 - Quy trình vào lệnh