Hướng dẫn lập trình Game C/C++ cho người mới bắt đầu

Hướng dẫn lập trình Game C/C++ cho người mới bắt đầu

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (944.36 KB, 45 trang )

1 | P a g e

Game C/C++ cho newbie – 01. Lập trình game bắt đầu từ đâu?
bởi Nguyễn Khánh Duy

Game là gì?

Nhìn chung, khi chưa từng biết qua lập trình game, chúng ta thường hay đặt những câu hỏi: lập trình
game là như thế nào? Có giống lập trình ứng dụng không? Có khó không? Có đòi hỏi những kỹ thuật gì
đặt biệt không ?

Câu trả lời của tôi là: game cũng chỉ là một ứng dụng, và nó cũng như bao ứng dụng khác. Khi viết một
ứng dụng, bạn có thể bắt đầu từ hàm main, hoặc dùng IDE sinh code tự động để hổ trợ. Game cũng vậy.
Bạn cũng có thể bắt đầu viết game từ hàm main, hoặc dùng các engine hổ trợ.

Đứng về khía cạnh lập trình, hay từ chủ quan của tôi, game đơn giản là một vòng lặp vô tận. Trong vòng
lập đó, bạn vẽ, bạn xử lý các diễn biến của game, xử lý sự kiện tương tác từ người chơi. Vậy là đủ cho
một game. Tuy nhiên, để làm game cho mục đích thương mại, đòi hỏi bạn nhiều hơn thế.

Một cách nhìn khác, game là một cuốn phim có tương tác. Nếu như một bộ phim cần nhiều thứ như kịch
bản tốt, dàn dựng hay, hậu kỳ, kiểm duyệt, quãng bá, thì game cũng vậy. Game cũng cần một nội
dung hay (kịch bản), coding tốt (dàn dựng, hậu kỳ), kiểm soát chất lượng (kiểm duyệt), quãng bá Nếu
một phim thành công được đánh giá qua doanh thu, thì game cũng vậy. Tuy nhiên, khi mới bắt đầu, thì
doanh thu, lợi nhuận, cần được gạt ra khỏi tư tưởng của mình, để có đủ tỉnh táo tập trung vào chuyên
môn.

Lập trình game bằng ngôn ngữ nào?

Như mọi ứng dụng khác, bạn có thể lập trình game bằng mọi ngôn ngữ. Tùy theo nhu cầu, sở trường mà
bạn có thể chọn một ngôn ngữ nào đó để làm game. Tuy nhiên, hiện nay có một vài xu thế như sau:

2 | P a g e

Java: thường dùng để viết game cho Mobile – các dòng phone hổ trợ J2ME, hoặc viết game cho Android.
Ít khi dùng để viết game cho PC
C#: khi nhắc tới C#, ta có thể nghĩ ngay đến XNA, và gắn liền với thương hiệu Microsoft. Dùng viết game
cho windows mobiles hoặc PC.
Javascript: dùng cho môi trường web
Objective C: Dùng cho iOS như máy MAC, iPhone, iPad.
C/C++: với sự lâu đời cũng như được sự hưởng ứng rộng rãi từ hầu hết các chương trình đào tạo đại
học, C/C++ được xem là ngôn ngữ cơ bản của mọi ngôn ngữ lập trình, và có lẽ ít nhất một lần trong đời
thì mỗi programmer đều từng đụng đến nó. Do đó, C/C++ cũng là một ngôn ngữ khá được ưa chuộng
trong lập trình game ngày nay, với khả năng thực thi trên khác nhiều platform: Windows, Linux, MacOS,
Android, iphone/iPad, Symbian, Brew, Meegoo,

Xin lưu ý là ta không nên đánh đồng C/C++ với Visual C, hay Turbo C, hay Visual studio. VC, TC, VS là
những IDE, còn C/C++ là ngôn ngữ lập trình. Ta có thể code C/C++ hoàn toàn bằng Nodepad, và dùng các
trình biên dịch khác nhau để build.

Trong nội dung bài này, tôi cũng dùng C/C++ trên môi trường Visual studio 2010 để minh họa. Các bạn
có thể download bản express tại đây. Không cần thiết phải crack.

Lập trình game cần những kiến thức gì?

Đã là lập trình hiển nhiên cần phải biết lập trình, tư duy lập trình. Không nhất thiết bạn phải xuất sắc;
biết ít, làm ít, biết nhiều, làm nhiều.

Một chút kiến thức kỹ năng về game. Hay nói cách khác, biết chơi game, và từng chơi game.
Biết một ngôn ngữ lập trình nào đó.
Biết một ít kiến thức về toán, vật lý (google it, nếu cần)

3 | P a g e

Một số kiến thức về đồ họa 2D, 3D
Trong phần này, ta sẽ bắt đầu code game.

Chuẩn bị

Bạn cần cài đặt một số tool cần thiết sau:

Visual studio express 2010
Notepad ++
Python 2.7

Java SDK 1.6

Cài đặt “Hello game”

Như đã trình bài trong bài trước, game là một vòng lập vô tận. Do đó, với chương trình như nhau, ta
cũng có thể tạm gọi là một game (nhưng chưa có tương tác)

Bước một, dùng visual studio, tạo một Empty project, tên gametutor:

4 | P a g e

Bước 2, tạo file main.cpp,

Add một item mới

5 | P a g e

Tạo main.cpp từ template

với nội dung:

1
2
3
4
#include “stdio.h”
int main()
{
while(true)

6 | P a g e

5
6
7
8
{
printf(“Hello Game\n”);
}
}

Frame delay

Đây được xem là bước cải tiến đầu tiên của “game” trên. Như bạn thấy, dòng hello game xuất hiện một
cách liên tục thông qua vòng lập. Mỗi vòng lặp như vậy, được gọi là một “frame”.

Trong thực tế, mắt người chỉ cần 24 frame/s là đủ để cảm nhận hiệu ứng “mượt mà”. Do đó để tránh
lãng phí CPU, cũng như nhường CPU cho các task khác, ta tạo ra khoảng delay giữa các frame. Khoảng
thời gian này là bao nhiêu, sẽ được giải thích rõ hơn trong các phần sau. Hiện tai, ta thử cho khoảng thời
gian cố định là nghĩ 80 ms.

1
2
3
4
5
6
7
8
9
10
#include “stdio.h”
#include “windows.h”

int main()

{
while(true)
{
printf(“Hello Game\n”);
Sleep(80);
}

7 | P a g e

11
}
Game C/C++ cho newbie – 03. Quản lý vòng đời game
bởi Nguyễn Khánh Duy

Giới thiệu

Với tư tưởng hướng đối tượng, trong bước này, ta sẽ thiết kế những lớp cần thiết cho việc quản lý
game.

Đối tượng đầu tiên ta thấy chính là game: lớp CGame

Class quản lý game (CGame)

Về cơ bản, ta lớp CGame cần có các phương thức:

Init: thiết lập các tham số cho game
Destroy: hủy game. Gọi trước khi kết thúc game, dùng để thu hồi vùng nhớ, giải phóng thiết bị chiếm
giữ,
Exit: yêu cầu kết thúc game. Làm này được người dùng gọi khi cần kết thúc game.
Run: Quản lý vòng lặp chính của game
Pause: tạm dừng game
Resume: khôi phục game sau khi tạm dừng.

8 | P a g e

Và một số thuộc tính:

[bool] m_isAlive: cho biết game còn “sống” hay không. Được thiết lập “true” khi init, và được thiết lập
false khi hàm exit được gọi.
[bool] m_isPaused: cho biết game có đang tạm dừng hay không. Được thiết lập false thi khi init. Giá trị
được cập nhật thông qua hàm Pause/Resume

1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
/// CGame.h

#ifndef __CGAME_H__
#define __CGAME_H__

namespace GameTutor
{
class CGame
{
public:
CGame();
virtual ~CGame() {}
virtual void Run();
virtual void Exit();
virtual void Pause();
virtual void Resume();

9 | P a g e

17

18
19
20
21
22
23
24
25
26
27
28
bool IsAlive() {return m_isAlived;}
bool IsPause() {return m_isPaused;}
protected:
virtual void Init() = 0;
virtual void Destroy() = 0;
protected:
bool m_isAlived;
bool m_isPaused;
};
}

#endif

1
2

3
4
5
6
7
8
9
10
11
/// CGame.cpp

#include “CGame.h”
#include “stdio.h”
#include “windows.h”

namespace GameTutor
{
CGame::CGame(): m_isPaused(false), m_isAlived(true){}

void CGame:: Pause()

10 | P a g e

12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
m_isPaused = true;
}

void CGame::Resume()
{
m_isPaused = false;
}

void CGame::Exit()
{
m_isAlived = false;
}

void CGame::Run()
{
this->Init();
while (m_isAlived)
{
if (m_isPaused)
{
printf(“paused\n”);
}
else
{
printf(“running\n”);
}
Sleep(80);

11 | P a g e

40
41
42
43
}
Destroy();
}
}

Trong ví dụ trên, ta thấy lớp CGame được thiết kế với 2 hàm thuần ảo. Điều này có nghĩa là ta không thể
khởi tạo trực tiếp lớp CGame. Để sử dụng lớp CGame, ta cần tạo một lớp thừa kế từ CGame. Điều này
đảm bảo cho sự tách biệt giữa lớp thư viện (CGame) và ứng dụng. Vì sao cần có sự tách biệt này?

Đảm bảo khả năng sử dụng lại của CGame ở nhiều game khác nhau.
Tách biệt giữ lớp thư viện và lớp ứng dụng, thuận tiện trong việc phát triển và bảo trì.

Khi này, ta thiết kế 1 lớp mới có tên là CExample, thừa kế từ CGame:

1
2
3
4
5
6
7
8
9
/// CExample.h
#ifndef __CEXAMPLE_H__
#define __CEXAMPLE_H__
#include “CGame.h”

using namespace GameTutor;

class CExample:public CGame
{

12 | P a g e

10
11
12
13
14
15
16
17
18
19
public:
CExample(): CGame() {}
virtual ~CExample() {}

protected:
void Init();
void Destroy();
};

#endif

1
2
3
4
5
6
7
8
9
10
11
12
13
//Example.cpp
#include “CExample.h”
#include “stdio.h”

void CExample::Init()
{
printf(“Init\n”);
}

void CExample: estroy()
{
printf(“Destroy\n”);
}

13 | P a g e

Hàm main được thiết kế đơn giản như sau:

1
2
3
4
5
6
7
/// main.cpp
#include “CExample.h”

int main()
{
(new CExample())->Run();
}
Game C/C++ cho newbie – 04.Chia để trị
bởi Nguyễn Khánh Duy

Phương pháp chung

Ở phần 3, ta đã thiết kế một lớp để quản lý vòng đời của game. Tuy nhiên, khó khăn dễ thấy là game
thường rất lớn. Nếu tất cả mọi cập nhật game đều để trong hàm Run(), thì việc quản lý trở nên rất khó
khăn. Để giải quyết vấn đề này, ta có thể áp dụng một phương pháp cũ nhưng hiệu quả: chia để trị.

Phương pháp chia để trị này được áp dụng trong tất cả các ứng dụng, thông qua việc áp dụng lượt
đồ State diagram:

14 | P a g e

Source: http://upload.wikimedia.org/wikipedia/commons/c/cf

Áp dụng mô hình trên, game sẽ được chia thành nhiều state. Vấn đề nãy sinh là state trong game là gì,
và chia như thế nào?

State và cách chia state trong game

State trong game, có thể hiểu là một giai đoạn của game, hay một màn hình/ 1 cảnh / 1 scene (tùy theo
cách gọi, cách hiểu).

Khi chơi một game, lấy vị dụ như game Angry Birds trên trình duyệt Chrome:

Logo nhà sản xuất

Màn hình giới thiệu game (poster)
Menu chính (gồm các nút play, option )
Các menu phụ chọn màn chơi
Loading trước khi vào màn chơi
Màn chơi

Mỗi giai đoạn như vậy, ta có thể gọi là một state, hay một scene. ta chọn khái niệm state để gần gũi với
mô hình UML.

15 | P a g e

State vs sub-State

Với một state quá lớn, ta lại nghĩ đến trường hợp chia nhỏ state thành các sub-state. Tuy nhiên, nên
thận trọng trong việc chia sub-state. Việc chia thành các sub-state bên trong state đòi hỏi chi phí quản lý
cao hơn. Do đó, không nên nếu không thật sự cần thiết.

Thiết kế State như thế nào ?

Nhìn chung, State cũng giống như một ứng dụng thu nhỏ, do đó cũng có các bước cơ bản sau:

Init
Run

Exit

Tuy nhiên, để tách biệt giữ xử lý logic và việc vẽ lên màn hình, Run nên được chia thành 2 bước nhỏ:
Update (dành cho xử lý logic) và Render (dành cho việc vẽ). Việc chia tách này mang lại cho ta nhiều lợi
ích. Bạn nào đã từng làm qua MVC, chắc hẳn biết được lợi ích của nó. Lúc này, các thao tác cần có của
một State bao gồm:

Init
Update
Render
Exit

16 | P a g e

1
2
3
4
5
6

7
8
9
10
11
12
13
14
/// CState.h
#ifndef __CSTATE_H__
#define __CSTATE_H__

namespace GameTutor
{
class CState
{
public:
CState(){}
virtual ~CState(){}
virtual void Init() = 0;
virtual void Update() = 0;
virtual void Render() = 0;

17 | P a g e

15
16
17
18
virtual void Exit() = 0;

};
}
#endif

Quản lý các State như thế nào?

Sau khi đã chia nhỏ các state, nhiệm vụ tiếp theo là làm sao đảm bảo việc chuyển đổi giữa các state
được trơn tru.

Việc quản lý các state nhìn chung là do vòng lặp chính điều khiển. Tuy nhiên để thuận tiên, ta có thể
định nghĩa 1 lớp chuyển quản lý các state, tạm gọi là lớp CStateManagement

Để đơn giản, việc quản lý state tuân theo nguyên tắc sau:

Tại một thời điểm, chỉ có 1 state được phép “hoạt động” (Update/Render)
Khi chuyển từ một state (A) sang một state khác (B), A phải được hủy (Exit) và B phải được tạo (Init) sau
đó
Chỉ chuyển state (chuyển sang state khác) khi State cũ đã kết thúc việc update & render.

1
2
/// CStateManagement.h

18 | P a g e

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
#ifndef __CSTATEMANAGEMENT_H__
#define __CSTATEMANAGEMENT_H__
#include “CState.h”

namespace GameTutor
{
class CStateManagement
{
public:
static CStateManagement* GetInstance()
{
if (!s_pIntance)
{
s_pIntance = new CStateManagement();
}
return s_pIntance;
}
protected:
static CStateManagement* s_pIntance;
protected:
CStateManagement():m_pCurrentState(0), m_pNextState(0) {}
protected:
CState* m_pCurrentState;
CState* m_pNextState;
public:
void Update(bool isPause);
void SwitchState(CState* nextState);
};

19 | P a g e

31
32
}
#endif

Trong file trên, CStateManagement được thiết kế theo kiểu Singleton pattern. Điều này đảm bảo lớp
CStateManagement tồn tại duy nhất 1 instance trong suốt game.

Tiếp theo, ta xem ví dụ mẫu về cách quản lý state thông qua hàm update và switch state:

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
/// CStateManagement.cpp

#include “CStateManagement.h”

namespace GameTutor
{
CStateManagement* CStateManagement::s_pIntance = 0;

void CStateManagement::Update(bool isPause)
{
// check if need switch state
if (m_pCurrentState != m_pNextState)
{
if (m_pCurrentState)
{

20 | P a g e

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
m_pCurrentState->Exit();
delete m_pCurrentState;
}
if (m_pNextState)
{
m_pNextState->Init();
}
m_pCurrentState = m_pNextState;
}

//update state
if (m_pCurrentState)

{
if (!isPause)
{
m_pCurrentState->Update();
}
m_pCurrentState->Render();
}
}

void CStateManagement::SwitchState(CState* nextState)
{
m_pNextState = nextState;
}
}

21 | P a g e

Trong ví dụ trên, hàm Update mới là hàm quản lý chính việc chuyển state. SwitchState chỉ đóng vai trò
“đánh dấu”. Điều này đảm bảo CStateManagement hoạt động đúng theo 3 tiêu chí đã nêu ở trên.

Kết nối State và Game

Do việc quản lý State lúc này được trao cho CStateManagement. Ta chỉ việc kết nối CStateManagement
và CGame.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// CGame.h
#ifndef __CGAME_H__
#define __CGAME_H__

namespace GameTutor
{
class CGame
{
public:
static CGame* GetInstance() {return s_pInstance;}
virtual ~CGame() {}
virtual void Run();

virtual void Exit();
virtual void Pause();
virtual void Resume();
bool IsAlive() {return m_isAlived;}
bool IsPause() {return m_isPaused;}
protected:
CGame();

22 | P a g e

20
21
22
23
24
25
26
27
28
29
static CGame* s_pInstance;
virtual void Init() = 0;
virtual void Destroy() = 0;
protected:
bool m_isAlived;
bool m_isPaused;
};
}

#endif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// CGame.cpp

#include “CGame.h”
#include “stdio.h”
#include “windows.h”
#include “CStateManagement.h”

namespace GameTutor
{
CGame* CGame::s_pInstance = 0;

CGame::CGame(): m_isPaused(false), m_isAlived(true)

{
s_pInstance = this;
}

23 | P a g e

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43

void CGame:: Pause()
{
m_isPaused = true;
}

void CGame::Resume()
{
m_isPaused = false;
}

void CGame::Exit()
{
m_isAlived = false;
}

void CGame::Run()
{
this->Init();
while (m_isAlived)
{
if (m_isPaused)
{
CStateManagement::GetInstance()->Update(true);
}
else

{
CStateManagement::GetInstance()->Update(false);

24 | P a g e

44
45
46
47
48
49
}
Sleep(80);
}
Destroy();
}
}

Ví dụ sử dụng State và chuyển state

Giả sử ta có 2 State:

State Logo: xuất ra màn hình từ 1 đến 10. Sau đó chuyển qua state Poster
State Poster: xuất ra màn hình từ 10 tới 1. Sau đó kết thúc game.

1
2
3
4
5
6
7
8
9
/// CStateLogo.h

#ifndef __CSTATELOGO_H__
#define __CSTATELOGO_H__
#include “CState.h”
using namespace GameTutor;

class CStateLogo: public CState
{

25 | P a g e

10
11
12
13
14
15
16
17

18
19
20
21
public:
CStateLogo();
~CStateLogo() {}

void Init();
void Update();
void Render();
void Exit();
private:
int m_iCount;
};
#endif

1
2
3
4
5
6
7
8
9
10
11

12
13
/// CStateLogo.cpp

#include “CStateLogo.h”
#include “CStateManagement.h”
#include “CStatePoster.h”
#include

CStateLogo::CStateLogo():m_iCount(0), CState()
{}

void CStateLogo::Init()
{
printf(“State Logo: Init\n”);
Lập trình game bằng ngôn từ nào ? Như mọi ứng dụng khác, bạn hoàn toàn có thể lập trình game bằng mọi ngôn từ. Tùy theo nhu yếu, sở trường màbạn hoàn toàn có thể chọn một ngôn từ nào đó để làm game. Tuy nhiên, lúc bấy giờ có một vài xu thế như sau : 2 | P a g eJava : thường dùng để viết game cho Mobile – những dòng phone hổ trợ J2ME, hoặc viết game cho Android. Ít khi dùng để viết game cho PCC # : khi nhắc tới C #, ta hoàn toàn có thể nghĩ ngay đến XNA, và gắn liền với tên thương hiệu Microsoft. Dùng viết gamecho windows mobiles hoặc PC.Javascript : dùng cho môi trường tự nhiên webObjective C : Dùng cho iOS như máy MAC, iPhone, iPad. C / C + + : với sự truyền kiếp cũng như được sự hưởng ứng thoáng rộng từ hầu hết những chương trình giảng dạy đạihọc, C / C + + được xem là ngôn từ cơ bản của mọi ngôn từ lập trình, và có lẽ rằng tối thiểu một lần trong đờithì mỗi programmer đều từng đụng đến nó. Do đó, C / C + + cũng là một ngôn từ khá được ưa chuộngtrong lập trình game ngày này, với năng lực thực thi trên khác nhiều platform : Windows, Linux, MacOS, Android, iphone / iPad, Symbian, Brew, Meegoo, Xin quan tâm là ta không nên đánh đồng C / C + + với Visual C, hay Turbo C, hay Visual studio. VC, TC, VS lànhững IDE, còn C / C + + là ngôn từ lập trình. Ta hoàn toàn có thể code C / C + + trọn vẹn bằng Nodepad, và dùng cáctrình biên dịch khác nhau để build. Trong nội dung bài này, tôi cũng dùng C / C + + trên môi trường tự nhiên Visual studio 2010 để minh họa. Các bạncó thể tải về bản express tại đây. Không thiết yếu phải crack. Lập trình game cần những kiến thức và kỹ năng gì ? Đã là lập trình hiển nhiên cần phải biết lập trình, tư duy lập trình. Không nhất thiết bạn phải xuất sắc ; biết ít, làm ít, biết nhiều, làm nhiều. Một chút kỹ năng và kiến thức kỹ năng và kiến thức về game. Hay nói cách khác, biết chơi game, và từng chơi game. Biết một ngôn từ lập trình nào đó. Biết một chút ít kỹ năng và kiến thức về toán, vật lý ( google it, nếu cần ) 3 | P a g eMột số kiến thức và kỹ năng về đồ họa 2D, 3DT rong phần này, ta sẽ mở màn code game. Chuẩn bịBạn cần thiết lập một số ít tool thiết yếu sau : Visual studio express 2010N otepad + + Python 2.7 Java SDK 1.6 Cài đặt ” Hello game ” Như đã trình bài trong bài trước, game là một vòng lập vô tận. Do đó, với chương trình như nhau, tacũng hoàn toàn có thể tạm gọi là một game ( nhưng chưa có tương tác ) Bước một, dùng visual studio, tạo một Empty project, tên gametutor : 4 | P a g eBước 2, tạo file main.cpp, Add một item mới5 | P a g eTạo main.cpp từ templatevới nội dung : # include ” stdio. h ” int main ( ) while ( true ) 6 | P a g eprintf ( ” Hello Game \ n ” ) ; Frame delayĐây được xem là bước nâng cấp cải tiến tiên phong của ” game ” trên. Như bạn thấy, dòng hello game Open mộtcách liên tục trải qua vòng lập. Mỗi vòng lặp như vậy, được gọi là một ” frame “. Trong thực tiễn, mắt người chỉ cần 24 frame / s là đủ để cảm nhận hiệu ứng ” mềm mại và mượt mà “. Do đó để tránhlãng phí CPU, cũng như nhường CPU cho những task khác, ta tạo ra khoảng chừng delay giữa những frame. Khoảngthời gian này là bao nhiêu, sẽ được lý giải rõ hơn trong những phần sau. Hiện tai, ta thử cho khoảng chừng thờigian cố định và thắt chặt là nghĩ 80 ms. 10 # include ” stdio. h ” # include ” windows. h ” int main ( ) while ( true ) printf ( ” Hello Game \ n ” ) ; Sleep ( 80 ) ; 7 | P a g e11Game C / C + + cho newbie – 03. Quản lý vòng đời gamebởi Nguyễn Khánh DuyGiới thiệuVới tư tưởng hướng đối tượng người tiêu dùng, trong bước này, ta sẽ phong cách thiết kế những lớp thiết yếu cho việc quản lýgame. Đối tượng tiên phong ta thấy chính là game : lớp CGameClass quản trị game ( CGame ) Về cơ bản, ta lớp CGame cần có những phương pháp : Init : thiết lập những tham số cho gameDestroy : hủy game. Gọi trước khi kết thúc game, dùng để tịch thu vùng nhớ, giải phóng thiết bị chiếmgiữ, Exit : nhu yếu kết thúc game. Làm này được người dùng gọi khi cần kết thúc game. Run : Quản lý vòng lặp chính của gamePause : tạm dừng gameResume : Phục hồi game sau khi tạm dừng. 8 | P a g eVà 1 số ít thuộc tính : [ bool ] m_isAlive : cho biết game còn ” sống ” hay không. Được thiết lập ” true ” khi init, và được thiết lậpfalse khi hàm exit được gọi. [ bool ] m_isPaused : cho biết game có đang tạm dừng hay không. Được thiết lập false thi khi init. Giá trịđược update trải qua hàm Pause / Resume10111213141516 / / / CGame. h # ifndef __CGAME_H__ # define __CGAME_H__namespace GameTutorclass CGamepublic : CGame ( ) ; virtual ~ CGame ( ) { } virtual void Run ( ) ; virtual void Exit ( ) ; virtual void Pause ( ) ; virtual void Resume ( ) ; 9 | P a g e171819202122232425262728bool IsAlive ( ) { return m_isAlived ; } bool IsPause ( ) { return m_isPaused ; } protected : virtual void Init ( ) = 0 ; virtual void Destroy ( ) = 0 ; protected : bool m_isAlived ; bool m_isPaused ; } ; # endif1011 / / / CGame. cpp # include ” CGame. h ” # include ” stdio. h ” # include ” windows. h ” namespace GameTutorCGame :: CGame ( ) : m_isPaused ( false ), m_isAlived ( true ) { } void CGame :: Pause ( ) 10 | P a g e12131415161718192021222324252627282930313233343536373839m_isPaused = true ; void CGame :: Resume ( ) m_isPaused = false ; void CGame :: Exit ( ) m_isAlived = false ; void CGame :: Run ( ) this -> Init ( ) ; while ( m_isAlived ) if ( m_isPaused ) printf ( ” paused \ n ” ) ; elseprintf ( ” running \ n ” ) ; Sleep ( 80 ) ; 11 | P a g e40414243Destroy ( ) ; Trong ví dụ trên, ta thấy lớp CGame được phong cách thiết kế với 2 hàm thuần ảo. Điều này có nghĩa là ta không thểkhởi tạo trực tiếp lớp CGame. Để sử dụng lớp CGame, ta cần tạo một lớp thừa kế từ CGame. Điều nàyđảm bảo cho sự tách biệt giữa lớp thư viện ( CGame ) và ứng dụng. Vì sao cần có sự tách biệt này ? Đảm bảo năng lực sử dụng lại của CGame ở nhiều game khác nhau. Tách biệt giữ lớp thư viện và lớp ứng dụng, thuận tiện trong việc tăng trưởng và bảo dưỡng. Khi này, ta phong cách thiết kế 1 lớp mới có tên là CExample, thừa kế từ CGame : / / / CExample. h # ifndef __CEXAMPLE_H__ # define __CEXAMPLE_H__ # include ” CGame. h ” using namespace GameTutor ; class CExample : public CGame12 | P a g e10111213141516171819public : CExample ( ) : CGame ( ) { } virtual ~ CExample ( ) { } protected : void Init ( ) ; void Destroy ( ) ; } ; # endif10111213 / / Example. cpp # include ” CExample. h ” # include ” stdio. h ” void CExample :: Init ( ) printf ( ” Init \ n ” ) ; void CExample : estroy ( ) printf ( ” Destroy \ n ” ) ; 13 | P a g eHàm main được phong cách thiết kế đơn thuần như sau : / / / main.cpp # include ” CExample. h ” int main ( ) ( new CExample ( ) ) -> Run ( ) ; Game C / C + + cho newbie – 04. Chia để trịbởi Nguyễn Khánh DuyPhương pháp chungỞ phần 3, ta đã phong cách thiết kế một lớp để quản trị vòng đời của game. Tuy nhiên, khó khăn vất vả dễ thấy là gamethường rất lớn. Nếu tổng thể mọi update game đều để trong hàm Run ( ), thì việc quản trị trở nên rất khókhăn. Để xử lý yếu tố này, ta hoàn toàn có thể vận dụng một chiêu thức cũ nhưng hiệu suất cao : chia để trị. Phương pháp chia để trị này được vận dụng trong toàn bộ những ứng dụng, trải qua việc vận dụng lượtđồ State diagram : 14 | P a g eSource : http://upload.wikimedia.org/wikipedia/commons/c/cfÁp dụng quy mô trên, game sẽ được chia thành nhiều state. Vấn đề nãy sinh là state trong game là gì, và chia như thế nào ? State và cách chia state trong gameState trong game, hoàn toàn có thể hiểu là một quá trình của game, hay một màn hình hiển thị / 1 cảnh / 1 scene ( tùy theocách gọi, cách hiểu ). Khi chơi một game, lấy vị dụ như game Angry Birds trên trình duyệt Chrome : Logo nhà sản xuấtMàn hình trình làng game ( poster ) Menu chính ( gồm những nút play, option ) Các menu phụ chọn màn chơiLoading trước khi vào màn chơiMàn chơiMỗi tiến trình như vậy, ta hoàn toàn có thể gọi là một state, hay một scene. ta chọn khái niệm state để thân thiện vớimô hình UML. 15 | P a g eState vs sub-StateVới một state quá lớn, ta lại nghĩ đến trường hợp chia nhỏ state thành những sub-state. Tuy nhiên, nênthận trọng trong việc chia sub-state. Việc chia thành những sub-state bên trong state yên cầu ngân sách quản lýcao hơn. Do đó, không nên nếu không thật sự thiết yếu. Thiết kế State như thế nào ? Nhìn chung, State cũng giống như một ứng dụng thu nhỏ, do đó cũng có những bước cơ bản sau : InitRunExitTuy nhiên, để tách biệt giữ giải quyết và xử lý logic và việc vẽ lên màn hình hiển thị, Run nên được chia thành 2 bước nhỏ : Update ( dành cho giải quyết và xử lý logic ) và Render ( dành cho việc vẽ ). Việc chia tách này mang lại cho ta nhiều lợiích. Bạn nào đã từng làm qua MVC, chắc rằng biết được quyền lợi của nó. Lúc này, những thao tác cần có củamột State gồm có : InitUpdateRenderExit16 | P a g e1011121314 / / / CState. h # ifndef __CSTATE_H__ # define __CSTATE_H__namespace GameTutorclass CStatepublic : CState ( ) { } virtual ~ CState ( ) { } virtual void Init ( ) = 0 ; virtual void Update ( ) = 0 ; virtual void Render ( ) = 0 ; 17 | P a g e15161718virtual void Exit ( ) = 0 ; } ; # endifQuản lý những State như thế nào ? Sau khi đã chia nhỏ những state, trách nhiệm tiếp theo là làm thế nào bảo vệ việc quy đổi giữa những stateđược trơn tru. Việc quản trị những state nhìn chung là do vòng lặp chính điều khiển và tinh chỉnh. Tuy nhiên để thuận tiên, ta có thểđịnh nghĩa 1 lớp chuyển quản trị những state, tạm gọi là lớp CStateManagementĐể đơn thuần, việc quản trị state tuân theo nguyên tắc sau : Tại một thời gian, chỉ có 1 state được phép ” hoạt động giải trí ” ( Update / Render ) Khi chuyển từ một state ( A ) sang một state khác ( B ), A phải được hủy ( Exit ) và B phải được tạo ( Init ) sauđóChỉ chuyển state ( chuyển sang state khác ) khi State cũ đã kết thúc việc update và render. / / / CStateManagement. h18 | P a g e101112131415161718192021222324252627282930 # ifndef __CSTATEMANAGEMENT_H__ # define __CSTATEMANAGEMENT_H__ # include ” CState. h ” namespace GameTutorclass CStateManagementpublic : static CStateManagement * GetInstance ( ) if ( ! s_pIntance ) s_pIntance = new CStateManagement ( ) ; return s_pIntance ; protected : static CStateManagement * s_pIntance ; protected : CStateManagement ( ) : m_pCurrentState ( 0 ), m_pNextState ( 0 ) { } protected : CState * m_pCurrentState ; CState * m_pNextState ; public : void Update ( bool isPause ) ; void SwitchState ( CState * nextState ) ; } ; 19 | P a g e3132 # endifTrong file trên, CStateManagement được phong cách thiết kế theo kiểu Singleton pattern. Điều này bảo vệ lớpCStateManagement sống sót duy nhất 1 instance trong suốt game. Tiếp theo, ta xem ví dụ mẫu về cách quản trị state trải qua hàm update và switch state : 101112131415 / / / CStateManagement. cpp # include ” CStateManagement. h ” namespace GameTutorCStateManagement * CStateManagement :: s_pIntance = 0 ; void CStateManagement :: Update ( bool isPause ) / / check if need switch stateif ( m_pCurrentState ! = m_pNextState ) if ( m_pCurrentState ) 20 | P a g e1617181920212223242526272829303132333435363738394041m_pCurrentState -> Exit ( ) ; delete m_pCurrentState ; if ( m_pNextState ) m_pNextState -> Init ( ) ; m_pCurrentState = m_pNextState ; / / update stateif ( m_pCurrentState ) if ( ! isPause ) m_pCurrentState -> Update ( ) ; m_pCurrentState -> Render ( ) ; void CStateManagement :: SwitchState ( CState * nextState ) m_pNextState = nextState ; 21 | P a g eTrong ví dụ trên, hàm Update mới là hàm quản trị chính việc chuyển state. SwitchState chỉ đóng vai trò ” ghi lại “. Điều này bảo vệ CStateManagement hoạt động giải trí đúng theo 3 tiêu chuẩn đã nêu ở trên. Kết nối State và GameDo việc quản trị State lúc này được trao cho CStateManagement. Ta chỉ việc liên kết CStateManagementvà CGame. 10111213141516171819 / / / CGame. h # ifndef __CGAME_H__ # define __CGAME_H__namespace GameTutorclass CGamepublic : static CGame * GetInstance ( ) { return s_pInstance ; } virtual ~ CGame ( ) { } virtual void Run ( ) ; virtual void Exit ( ) ; virtual void Pause ( ) ; virtual void Resume ( ) ; bool IsAlive ( ) { return m_isAlived ; } bool IsPause ( ) { return m_isPaused ; } protected : CGame ( ) ; 22 | P a g e20212223242526272829static CGame * s_pInstance ; virtual void Init ( ) = 0 ; virtual void Destroy ( ) = 0 ; protected : bool m_isAlived ; bool m_isPaused ; } ; # endif101112131415 / / / CGame. cpp # include ” CGame. h ” # include ” stdio. h ” # include ” windows. h ” # include ” CStateManagement. h ” namespace GameTutorCGame * CGame :: s_pInstance = 0 ; CGame :: CGame ( ) : m_isPaused ( false ), m_isAlived ( true ) s_pInstance = this ; 23 | P a g e16171819202122232425262728293031323334353637383940414243void CGame :: Pause ( ) m_isPaused = true ; void CGame :: Resume ( ) m_isPaused = false ; void CGame :: Exit ( ) m_isAlived = false ; void CGame :: Run ( ) this -> Init ( ) ; while ( m_isAlived ) if ( m_isPaused ) CStateManagement :: GetInstance ( ) -> Update ( true ) ; elseCStateManagement :: GetInstance ( ) -> Update ( false ) ; 24 | P a g e444546474849Sleep ( 80 ) ; Destroy ( ) ; Ví dụ sử dụng State và chuyển stateGiả sử ta có 2 State : State Logo : xuất ra màn hình hiển thị từ 1 đến 10. Sau đó chuyển qua state PosterState Poster : xuất ra màn hình hiển thị từ 10 tới 1. Sau đó kết thúc game. / / / CStateLogo. h # ifndef __CSTATELOGO_H__ # define __CSTATELOGO_H__ # include ” CState. h ” using namespace GameTutor ; class CStateLogo : public CState25 | P a g e101112131415161718192021public : CStateLogo ( ) ; ~ CStateLogo ( ) { } void Init ( ) ; void Update ( ) ; void Render ( ) ; void Exit ( ) ; private : int m_iCount ; } ; # endif10111213 / / / CStateLogo. cpp # include ” CStateLogo. h ” # include ” CStateManagement. h ” # include ” CStatePoster. h ” # includeCStateLogo :: CStateLogo ( ) : m_iCount ( 0 ), CState ( ) { } void CStateLogo :: Init ( ) printf ( ” State Logo : Init \ n ” ) ;

5/5 - (1 vote)

Bài viết liên quan

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments