You are on page 1of 38

Copyrights by HuyetSat - Xvna.forumb.

biz - 2010
Nếu muốn tự mình viết trò chơi máy tính thì đây sẽ là tài liệu cần thiết cho bạn. Nó sẽ
hướng dẫn bạn lập trình xna từ những điều cơ bản nhất đến khi bạn có thể viết một
game hoàn chỉnh! Bạn cần phải biết qua về C# trước khi đọc tài liệu này. Đây là một
tài liệu hữu ích cho những newbie chưa từng viết game! Các game mình hướng dẫn rất
đơn giản (GetSix, Pong, Asteroids) để newbie dễ hiểu. Cái gì cũng phải bắt đầu từ đơn
giản trước. , bác sĩ cũng phải học các mổ Amiđan trước khi được phân công vào một
ca giải phâu não 

Xvna.forumb.biz là diễn đàn của tài liệu này, nếu cần thêm thông tin có đề cập đến
trong tài liệu, vui lòng truy cập vào diễn đàn.

Bản quyền thuộc về HuyetSat - Xvna.forumb.biz . yêu cầu bạn ghi rõ nguồn gốc nếu
bạn muốn phát hành tài liệu này trên trang web hoặc forum cá nhân của bạn.

==========================================

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Copyrights by Xvna.forumb.biz © - 2010
VietNam XNA Game Development Community!
Written by HuyetSat.
Chương 1:
Phát triển game là gì?, XNA là gì và tại sao lại viết game trên xna.

1.Tại sao lại cần phát triển game?


Đây là một số lý do chính:
+Doanh thu từ game cao hơn nhiều so với phim ảnh!
+Lập trình game là một thách thức lớn về mặt kỹ thuật!
+Lập trình game là môt công việc thú vị!
2.Viết game như thế nào?
Có 2 cách chính nếu bạn muốn viết game:
+Sử dụng một số engine như GameMaker, RPG Maker, FPS Creator, ... Nói chung là
chỉ cần có ý tưởng và tài nguyên là được. những engine trên hok yêu cầu bạn phải biết
lập trình! Bạn chỉ cần Drag và Drop object , chọn thêm method có sẵn trong engine thế
là bạn đã có game! Nói chung là ko nên sài mấy engine này! Lập trình chính là xương
sống của GameMaking chứ hok phải design!

+Học về C, C++ ,..., hệ thống đồ họa API, vât lý, toán học cao cấp. những kiến thức
rất khó nhưng cũng rất hữu ích! Tuy vậy tài liệu này viết cho newbie nên chúng ta
cũng ko bắt đầu với nó :>

Cái chúng ta bắt đầu là C# và xna 3.1 ! nó đơn giản nhưng cũng hok kém phần hiệu
quả trong lập trình game!

Yêu cầu của một Game Project hoàn chỉnh:


+Game đó phải chơi được! tức là đồ họa khá, hoạt hình mượt, có nội dung, có thắng
thua, kết hợp với hiệu ứng âm thanh.
+Phải có một kế hoạch rõ ràng: Tốt nhất các bạn viết hết các ý tưởng và thuật toán mà
các bạn nghĩ ra lên giấy, sau đó lọc ra những cái khả thi, mở xna lên và bắt đầu code!
+ Nghiên cứu kỹ cách bạn sẽ code: Code là một nghệ thuật! có nhiều cách để lập trình
tức là bạn sẽ thấy có nhiều thuật toán cho một vấn đề, cái chính là bạn phải chọn ra

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
một giải pháp hiểu quả nhất (Đơn giản, tốn it tài nguyên khi run, phải hiệu quả ...) cái
này mấy chương sau mình sẽ đưa ra ví dụ cụ thể.

3. XNA là gì và Tại sao lại là C# và XNA?

XNA là một công nghệ làm game của microsoft phát triển. nó hướng đến đối tượng sử
dụng là các học sinh, sinh viên và những người đam mê lập trình game... Bản XNA
3.1 yêu cầu bạn phải có visual studio 2008 trở lên (bạn có thể down bản Express 2008
SP1) tại trang web của microsoft và bộ XNA 3.1 (hoàn toàn miễn phí). Do XNA sử
dụng C# nên bắt buộc bạn phải có nền tảng C# tốt. nếu chỉ làm game đơn giản thì bạn
nắm vững phần lập trình hướng đối tượng của C# là ok! Sau khi cài đặt XNA, nó sẽ
tích hợp trực tiếp vào visual studio C# , bạn có thể chọn new project => XNA 3.1
Window game. XNA có 1 ưu điểm là viết game đc cho Xbox, Zune game vẫn chạy
trên HĐH Window mobile mấy cái này quan trọng lắm nhé, nếu bạn muốn kiếm tiền
từ việc viết game thì Xbox là môi trường hấp dẫn ko thua PC. Nếu game bạn viết hay
thì có thể mang lên chợ game XBox của microsoft mà bán cho những người chơi game
khác.

Search Các bài hướng dẫn training về C# và xna tại xvna!

4. Giao diện sử dụng cho coder khi bạn làm việc trên Visual C# 2008 express
edition!

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

5.Hướng dẫn Download và cài đặt xna.

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Chương 2:
Các thành phần cơ bản của xna
1.Hệ tọa độ
New bie các bạn nên bắt đầu với trục tọa độ 2D của XNA.
Gốc (0:0) nằm ở đỉnh trên cùng, bên trái của màn hình game khi bạn run game!
Trục Ox hướng từ trái sang phải theo chiều dương
Trục Oy hướng từ trên xuống dưới theo chiều dương
Giá trị tọa độ có thể là số thực (float)

Nhờ có hệ tọa độ mà bạn có thể tạo ra vị trí (position) vận tốc (velocity), gia tốc
(accelerate), trọng lực (gravity) ...

2.các thành phần Game, GameService và Game Component

Ko bắt buộc bạn phải sử dụng, tuy nhiêu nếu bạn dùng chúng thì chương trình của
bạn sẽ đơn giản đi rất nhiều!

2.1. Game, Service và những điều cần lưu ý


Gốc gác của XNA là 1 class được xây dựng , chính là
Microsoft.Xna.Framework.Game (gọi tắt là Game) Bạn sẽ thấy 1 file là program.cs sẽ
run cái Game1.cs này và nó chính là file chứa toàn bộ mã nguồn chính để chạy game,
nó bao gồm mọi thành phần của game, thường thì chúng ta cần 1 thể hiện của class
Game1 là đủ.
Trong class game chính của bạn (đc kế thừa từ Game) sẽ thấy có rất nhiều method mà
Game cho phép bạn override: initilize(), Update() ...
Tuy nhiên khởi đầu cho việc lập trình là khai báo biến, bạn nên khai báo các biến như
là Player, hay Enemy... nhiều Enemy thì có thể tạo 1 class chứa toàn bộ tụi nó gọi là
Enemy Manager chả hạn, nếu bạn có nhiều màn trong game thì chúng ta khai báo
player , Enemy trong từng màn (dùng GameCompoment - sẽ nói sau) và khai báo màn
ở file game chính (như mặc định là Game1.cs đó)
Bạn có thể cấp dữ liệu về một thứ dùng chung nào đó cho các GameCômpoment bằng
cách dùng service:

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Services.AddService(typeof(SpriteBatch), spriteBatch);

GameCompoment sẽ có giá trị của spriteBatch mà dùng cho 2D graphic ở Draw


method
Tiếp đến bạn có method Initilize() nó sẽ được gọi trước Update hay Draw method,
bạn thiết lập các giá trị cơ bản như: chạy nhạc nền hay tùy chọn full màn hình, thiết
lập cho Enemy và player nếu có...
LoadContent là method giúp bạn load tài nguyên từ thư mục Content (theo mặc định
sẽ là thư mục gốc chứa tài nguyên). Cú pháp để load khá đơn giản. thực ra bạn load ở
đâu cũng đc nhưng thường là sau khi Init và khi đã bắt đầu chạy game (vào Update)
thì chả ai lại load lại content cả, nó sẽ có thể gây ra những lỗi nghiêm trọng
Update là method có nhiệm vụ duyệt tất cả các thành phần của game, KT điều kiển để
Update cho game (VD tốc độ của object là 1, sau 1 lần update , tọa độ của nó sẽ cộng
thêm bởi 1) Khi dùng Update bạn nên cẩn thận đừng để quá nhiều object Update 1 lúc
game sẽ rất lắc. bạn nên KT điều kiện mà sẽ Update cho object (VD như chỉ Update
cho những Object nằm trong vùng nhìn thấy của camera khi làm game 3D) Theo mặc
định thì XNA update 60 lần 1 giây bạn có thể tăng giảm con số này tùy thix cho phù
hợp với game và sức chịu đựng của cái máy bạn: code dưới đây làm XNA update 100
lần 1 giây

Draw Method
là cái bạn cần chú ý sau cùng, nó cũng hết sức quan trọng vì mọi thứ bạn nhìn thấy
trong game đều đc đưa lên ở đây . tất nhiên điều này là ko bắt buộc bạn có thể để nó
trong Update, tuy nhiên XNA đã tạo sẵn cho chúng ta rùi, ngu gì ko sài và làm chương
trình đơn giản hơn? Draw cũng đc "update" như ở Update() method nhưng chỉ là khi
có sự thay đổi thích hợp liên quan đến Update method nghĩa là bạn cũng KT ĐK đc ở
đây nhưng chỉ là để Draw object cho thix hợp thôi, còn về game lôgic thì để ở
Update() là hay nhất
Bạn có thể thắc mắc về mấy chữ Base trong môi method. Nhiệm vụ của chúng rất đơn
giản thôi, đó là gọi các method tương ứng của các gamecompoment trong game (nếu
có) ví dụ như Update Method (sau khi Update cho Game1.cs xong, phần
Base.Update(gameTime) sẽ update cho tất cả các GameCompoment đã đăng ký (khởi
tạo và added vào mảng Compoments của class Game1.cs)
2.2.GameCompoment và DrawableGameCompoment
Cach tạo ra GameComponent, chọn như sau: kích phải vào project trong cửa sổ Solution...

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

Sau đó chọn XNA 3.1 >> GameComponent >> gõ tên lớp vào trường Name.

Visual sẽ tạo cho bạn một lớp kế thừa từ GameComponent có tên lớp bạn vừa gõ vào
trong trường Name.

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Components là một danh sách List<(Drawable)GameCompoment> của Game, nó đc
tạo sẵn cho bạn. những gì bạn cần làm là tạo class kế thừa từ
(Drawable)GameCompoment và add những thể hiện của class vừa viết này vào mảng
Components và nó sẽ đc tự động Update hay Draw trong những method
Base.Update(GameTime) hay Base.Draw(GameTime) của Game !

Giải thích kỹ hơn một tý thì nó là thế này:


Chúng ta sử dụng (Drawable)GameComponent, class sẵn có của XNA. Dùng nó rất có
lợi bởi lẽ trong Class Game1.cs của chúng ta, XNA đã tạo một List có tên
Components. Mỗi Đối tượng là GameComponent hay là DrawableGameComponent
khi đc Add vào mảng này sẽ đc tự động chạy các method quan trọng như initilize,
update (nếu là Drawable thì có thêm Draw method). Nghĩa là nếu bạn tạo ra 100 object
kiểu này thì sẽ đỡ công code, chúng ta chỉ cần đưa nó vào mảng Component là xong!
Mặt khác sử dụng Class component trên chúng ta còn có 1 tham số quan trọng đó là
Game, tức là Game1.cs đó khi XNA tạo sẵn cho bạn lớp Game1.cs có phải nó kế thừa
từ Microsoft.Xna.Framework.game. Khi nhập dữ liệu (bằng 1 method trùng tên với tên của
class) trong Game1.cs (thuờng ở LoadContent Method). Khi đã có Game trong tay bạn
có thể dễ dàng lấy dữ liệu từ Game1.cs truyền cho các GameComponent (thông qua
Service).
Khi
using Microsoft.Xna.Framework.GamerServices;
nghĩa là chúng lấy khả năng cho phép XNA truyền dữ liệu từ chuơng trình chính
(Game1.cs) sang Các gameComponent

Các gameComponent là những phần tạo lên 1 game hoàn chỉnh đc viết cho XNA. Bạn
sẽ thấy chúng luôn có một biến đc nhập vào, đó là game và khi đó mỗi
GameCompoment sẽ có sẵn một biến tên là Game (nó liên quan trực tiếp) đến
game1.cs . VD bạn có thể thêm GameCompoment object vào game chính như sau:
Game.Compoments.Add(object);
Drawable cũng như GameCompoment, cái khác là nó có khả năng Draw mà ko cần
thêm code ở game chính làm game code của chúng ta đơn giản đi rất nhiều.
GameCompoment dành cho những thứ ko cần draw như camera, light, materials, input
...
Biến Game thực sự rất hữu ích, khi mà bạn cần các giá trị từ game chính (VD:
Game.GraphicDevice - nếu ko có nó thì đố bạn tìm được giá trị aspectRatio mà làm ko
gian chiếu phối cảnh khi viết game 3D)

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Ngoài ra nó có thể nhận giá trị mà Game chính truyền cho thông qua service, theo như
VD phần 1:
( ở phần 1, trong class game1.cs:
Services.AddService(typeof(SpriteBatch), spriteBatch); )
Lấy service ở gameComponent:
spriteBatch = Game.Services.GetService(typeof(SpriteBatch))
nếu bạn dùng Drawable, bạn sẽ dùng cái spriteBatch cho Draw method, từ đó
Compoment sẽ có khả năng tự Draw kiểu như :
spriteBatch.Draw(texture,position,Color.White)

chỉ cần base.Draw(gameTime) là đủ để Vẽ tất cả DrawableGameCompoment trong


game là xong!

Tất cả những thứ liên quan đến service tốt nhất bạn sờ đến nó ở initilize() lúc này init
của game chính đã chạy và biến truyền dữ liệu đó ko bị null, bạn sẽ ko mất thời gian
debug mấy lỗi vớ vẩn như thế :D

Ngoài ra, hai thuộc tính của GameCompoment là Enable và Visiable, nếu chúng là
false thì trong phần Base ở các method Update và Draw trong class Game chính sẽ ko
chạy (tức là nó ko hề hoạt đông và ko nhìn thấy)

3. Sử dụng Texture, Audio, Video

Xem tại mục XNA cơ bản - Xvna.forumb.biz

Chương 3: Code Game Đầu tiên của bạn! - GetSix

1.Ý tưởng của game!

Đây là tài liệu dành cho newbie, nên game rất đơn giản. newbie nên tự coi và tự code
game, tuy đơn giản những từ những game đơn giản này, bạn sẽ tích lũy nhiều kinh

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
nghiệm cho nhưng game phức tạp hơn! Mình sẽ cố gắng code thật dễ hiểu và khoa học
cho các bạn tiện theo dõi.

GetSix là trò chơi hết sức đơn giản. bạn sẽ quay súc xắc và nếu đc 6 điểm bạn sẽ
chiến thắng, nếu ko game sẽ đưa ra số điểm bạn đổ đc và tạo cơ hội cho bạn đổ súc sắc
một lần nữa. :> Nào bắt tay vào việc luôn chứ nhẩy :)

2.Tạo tài nguyên cho game!

Kich phai chuột vào mục Content trong cửa sổ solution >> Add new Item >> Sprite
Font >> đặt tên cho Font là "font" >> Add

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

3.Lập trình cơ bản cho GetSix

3.1 Tao số ngẫu nhiên!

Tạo số ngẫu nhiên thường là phần cơ bản của game, từ số ngẫu nhiên chúng ta tạo ra
vị trí, tốc độ, số lượng, chỉ số sức mạnh... của đối tượng trong game một cách ngẫu
nhiên! Người chơi ko biết đc một số ngẫu nhiên là bao nhiêu, điều này tạo ra hứng thú
cho họ

Class mình xây dựng để tạo số ngẫu nhiên, sử dụng method Get(num) nhằm tạo 1 số
nguyên ngẫu nhiên từ 0 đến num
///
namespace GetSix_Xvna.forumb.biz
{
static class GetRanDom
{
static Random random = new Random();

public static int Get(int num)


{
random.GetHashCode();
return random.Next(num)+1;
}

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
}
3.2 Điều khiển Input từ keyBoard bằng kỹ thuật Press và Release!

Class này xây dựng nhằm mục đích check input dễ dàng hơn cho player.
Method Press(key) xác định khi một key dc nhấn xuống! method trả về true trong suốt
quá trình key được nhấn!
Method Release(Key) xac dinh khi một key được nhấn xuống, sau đó nó được nhả ra,
khi nó nhả ra thì method này mới trả về true
Method Update() phải được gọi trong Update(GameTime) của class Game của bạn!
///
namespace GetSix_Xvna.forumb.biz
{
class Input
{
KeyboardState keyBoard ;
KeyboardState lastKeyBoard = Keyboard.GetState();

public void Update()


{
lastKeyBoard = keyBoard;
keyBoard = Keyboard.GetState();
}
public bool Press(Keys key)
{
return keyBoard.IsKeyDown(key);
}
public bool Release(Keys key)
{
return (keyBoard.IsKeyUp(key) && lastKeyBoard.IsKeyDown(key));
}
}
}

3.3 Hoàn thiện trò chơi trong class Game chính!

Dưới đây là class chính cho trò chơi GetSix!


namespace GetSix_Xvna.forumb.biz
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Cac bien ban khai bao':
int number = 0;
Input input = new Input();
SpriteFont font;
Đây là hàm cấu trúc cho game:
public Game1()
{

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";

}
Chúng ta thiết lập spriteBatch ở đây, load font từ Content ở đây cũng được!
protected override void Initialize()
{
//Thiết lập cho spriteBatch của bạn ở đây, chúng ta sẽ dùng nó trong //việc Draw text lên
game
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load font từ content
font = Content.Load<SpriteFont>("font");
base.Initialize();
}
Method Update này sẽ được gọi đi gọi lại trong suốt quá trình chạy game, mục đích là
Kiểm tra tính lôgic của game thông qua các câu lệnh mà bạn code!
protected override void Update(GameTime gameTime)
{
//Update cho Input
input.Update();
//Nhan space de quay suc xac
if (input.Release(Keys.Space))
//So diem suc xac la` 1 den 6!
number = 1 + GetRanDom.Get(5);
//Thoat neu nhan esc
if (input.Release(Keys.Escape))
Exit();

base.Update(gameTime);
}

Method Draw(GameTime) này làm nhiệm vụ đưa đồ họa lên màn hình, nhớ phải
begin() spriteBatch trước khi bạn muốn Draw bất cứ thứ gì lên màn hình và End() sau
khi đã Draw hết mọi thứ bạn muốn.
protected override void Draw(GameTime gameTime)
{
//Thiết lập màu nền là CornflowerBlue!
GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin();

DrawText("Chao mung ban den voi tro choi Do suc xac!", new Vector2(100,
200),Color.Red);

if (number == 0)
DrawText("Moi ban quay so", new Vector2(100, 300), Color.Purple);
else
if(number == 6)
DrawText("Xin chuc mung, ban da chien thang khi quay dc so 6", new
Vector2(100, 300), Color.Pink);
else DrawText("Diem so hien tai ma ban quay duoc: " + number, new
Vector2(100, 300), Color.PowderBlue);

DrawText("Nhan Space de bat dau quay so!", new Vector2(150, 400),


Color.PapayaWhip);
DrawText("Nhan ESC de thoat game!", new Vector2(150, 500), Color.OrangeRed);

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
spriteBatch.End();

base.Draw(gameTime);
}
Method DrawText , Draw nội dung , vị trí viết chữ và màu chữ
public void DrawText(string st, Vector2 pos,Color color)
{
spriteBatch.DrawString(font, st, pos, color);
}

Xong rùi đó!

Chương 4: Viết một Game có tính tương tác cao - Pong!

1.Tại sao lại là pong?

Pong là một trò chơi cổ điển, Chính xác thì nó là game đầu tiên của thế giới! tuy vậy
pong lại là một trò chơi thú vị, có tính tương tác cao và có đủ yếu tố của một game
hoàn chỉnh. Thêm nữa code trò pong khá đơn giản nên newbie có thể nắm được mã
lệnh một cách dễ dàng, từng đó bạn sẽ rút ra đc kinh nghiệm lập trình xna cho bản thân

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Trong pong sẽ có 2 cây vợt (pad) và 1 quả bóng (ball). Người chơi hoặc AI sẽ điều
khiển vợt, đánh bóng và nếu đối phương ko đỡ đc quả bong bạn đánh về phía họ, bạn
sẽ ghi được 1 điểm!

Chúng ta sẽ xây dựng 2 phiên bản cho pong: player vs player và player vs AI !

2. Chuẩn bị tài nguyên

Ko có gì đặc biệt cả, các bạn chỉ cần kiểu như:


: Trái banh để đánh

: Cây vợt

1 Sprite Font (Cách tạo có ở chương trước)

3. Code Game!

Bạn nhận thấy cả ball và pad đều chuyển động, nếu muốn một vật chuyển động nó
phải có 2 yếu tố là vị trí và vận tốc. bạn cũng cần kiểm tra va chạm của cả vợt và bóng
trong quá trình chơi đúng ko nào? vậy chúng ta đã tìm ra những điểm chung của pad
và ball, do đó sẽ rất tiện nếu chúng ta xây dựng 1 lớp cơ sở là Sprite từ đó pad và ball
sẽ kế thừa từ Sprite!
Hãy nhớ thứ tự code game: Bao giờ cũng phải code lớp cơ sở trước, sau đó đến các
lớp mở rộng của nó và cuối cùng mới là class game chính !

Sprite là gì? Nói một cách nôm na thì sprite và những vật thể có thể di chuyển và
tương tác trong game (người chơi, kẻ thù, NPC...). Phân biệt nó với static hay solid
object, tức là những đối tượng cứng ko tương tác hay thay đổi trong game (như bức
tường, hòn đá...).

3.1 Sprite class

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Dưới đây là chi tiết cho lớp Sprite:

namespace Pong_Xvna.forumb.biz
{
/// <summary>
/// This is a game component that implements IUpdateable.
/// </summary>
public class Sprite : Microsoft.Xna.Framework.DrawableGameComponent
{
Điểm chung của ball và pad sẽ được thể hiện ở đây.
Texture (một bức ảnh 2D bao phủ lên đối tượng trong game)
Vị trí, vận tốc, SpriteBatch làm nhiệm vụ Draw Sprite!
Điểm Center (trả về tọa độ tâm của Sprite) làm cơ sở cho thuật toán nảy cho trái banh!
Đánh dấu là protected để đảm bảo lớp mở rộng của bạn có thể sử dụng lại những biến
này!
protected Texture2D texture;
protected Vector2 position, velocity;
protected SpriteBatch sp;
Hãy tạo thói quen xây dựng thuộc tinh, tất nhiên, bạn có thể đánh dấu là public cho
position và velocity, tuy thế với 1 game phức tạp , thuộc tính sẽ làm bạn dễ dàng nhận
ra giá trị hơn nhiều so với biến. Tạo thói quen đặt tên : viết thường cho biến và viết
hoa chữ cái đầu tiên cho thuộc tính! Như là 1 quy tắc ngầm vậy và bạn sẽ ko bao giờ
nhầm lẫn giữa 2 loại này.
public Vector2 Center
{
get { return position + new Vector2(texture.Width / 2, texture.Height / 2); }
}
public Vector2 Position
{
get { return position; }
set { position = value; }
}
public Vector2 Velocity
{
get { return velocity; }
set { velocity = value; }
}
Method cấu trúc cho sprite, cái "base" ở đây là do nó kế thừa từ
DrawableGameComponent!
public Sprite(Game game,Vector2 pos, Vector2 velo)
: base(game)
{
this.position = pos;
this.velocity = velo;
Sử dụng Service lấy từ class Game chính, sau này khi code class game chính, chúng ta
sẽ Add service, bây giờ cứ GetService cái đã !
sp = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
mã lệnh thể hiện List Component của Class game chính sẽ add Sprite vào, sau này nó
sẽ được tự động Update và Draw, bạn khỏi phải dài dòng code thêm trong class game
chính (Game ở đây là biến có liên quan trực tiếp đến Game chính của bạn (Game1.cs)
Game.Components.Add(this);
}

Bây giờ đến thuật toán va chạm. do đặc thù của pad là object pad có dạng hcn nên
chúng ta viết method trả về hcn bao quanh lấy pad! Như dưới đây:
public Rectangle getBound()
{
return new Rectangle((int)position.X, (int)position.Y, texture.Width,
texture.Height);
}
Để xác định va chạm giữa 2 Sprite, ta chỉ việc xem xem hcn bao quanh 2 Sprite đó có
trùng nhau hay ko! Intersects() method sẽ xác định việc đó và hành dưới đây sẽ xác
định xem 2 Sprite có va chạm nhau hay ko! Quá đơn giản phải không bạn.
public bool CheckCollides(Sprite s)
{
return getBound().Intersects(s.getBound());
}
Không cần Update gì nhiều, đoạn code này bạn cũng hiểu là khi velocity !=
vector2.Zero thì vật sẽ di chuyển theo 2 trục Ox và Oy theo các giá trị velocity.X và
velocity.Y
public override void Update(GameTime gameTime)
{
position += velocity;

base.Update(gameTime);
}
Quá đơn giản! đưa sprite lên màn hinh của người chơi.
public override void Draw(GameTime gameTime)
{
sp.Draw(texture, position, Color.White);
base.Draw(gameTime);
}
}
}

3.2 Xây dựng lớp Pad (player control)

Như bạn thấy, pad kế thừa từ sprite!


public class Pad : Sprite
{
Input, chúng ta cũng sẽ getService nó từ Game chính!

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Index là chỉ số cho người chơi, do index khác nhau nên sau này mã lệnh để Check
Keyboard cũng sẽ khác nhau.
Point là số điểm người chơi, dựa vào đó mà xác định thắng thua.
Ở đây pad sẽ có chuyển động gia tốc để tăng tính hấp dẫn , gia tốc = 2.
Input input;
int index;
int point = 0;

float accel = 2f;

public int Point


{
get { return point; }
set { point = value; }
}
public Pad(Game game,Vector2 pos,Vector2 velo,int index)
: base(game,pos,velo)
{
this.index = index;
Đây là class riêng cho pad, bạn có thể load texture ở đây, tuy vậy ở Sprite thì ko được
load Texture do nó là đồ dùng chung của pad và ball
texture = Game.Content.Load<Texture2D>("pad");
input = (Input)Game.Services.GetService(typeof(Input));
}

Như bạn thấy , tùy theo index mà sẽ có cách Check KeyBoard khác nhau. Vận
tốc thay đổi bởi gia tốc, do đó pad sẽ di chuyển theo kiểu chậm dần đều hay nhanh dần
điều!
public override void Update(GameTime gameTime)
{
//Pad chuyển dộng lên trên
if (index == 1 ? input.Press(Keys.Up) : input.Press(Keys.W))
velocity.Y -= accel;
//Pad chuyển động xuống dưới
else if (index == 1 ? input.Press(Keys.Down) : input.Press(Keys.S))
velocity.Y += accel;
//Do có gia tốc, ta phải giới hạn lại vân tốc! giá trị chỉ nằm trong khoảng [-7,7]
velocity.Y = MathHelper.Clamp(velocity.Y, -7, 7);

Vector2 lastPos = position;


//Base se tien hành : position+= velocity;
base.Update(gameTime);
//Ngan chan pad ra khoi man hình (kick thước: 800 x 500)
if (position.Y < 0 || position.Y > 500 - texture.Height)
position = lastPos;
}

3.3 Ball Class.

public class Ball : Sprite


{

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
public Ball(Game game,Vector2 pos, Vector2 velo)
: base(game,pos,velo)
{
//load texture
texture = Game.Content.Load<Texture2D>("ball");
}

Ball sẽ đảo tốc độ Y nếu bóng chạm biên!, tại sao chúng ta ko KT tọa X? đơn giản là
Khi bóng khi quá màn hình ở trục X tức là 1 bên đã ghi điểm, tốt nhất phần ghi điểm
chúng ta hãy code ở class Game chính.
public override void Update(GameTime gameTime)
{
if (position.Y < 0 || position.Y > 500 - texture.Height)
velocity.Y *= -1;

base.Update(gameTime);
}

3.4 Class Game Chính (Game1.cs)


Class này Kế thừa từ Game của xna!
public class Game1 : Microsoft.Xna.Framework.Game
{
Chúng ta khai báo pad, ball, font (Để ghi điểm người chơi), input (class từ chương
trước)
GraphicsDeviceManager graphics;
SpriteFont font;
SpriteBatch spriteBatch;
Input input = new Input();

Pad player1, player2;


Ball ball;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Đặt kick thước màn hình:
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 500;
}

Chủ yếu là thiết lập cho các biến đã khai báo ở trên. Đặt 2 pad tại 2 đầu màn hình theo
tọa độ của nó, truyền cho ball tốc độ X dương nhằm cho biết player bên trái sẽ là
người giao bóng!
protected override void Initialize()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
Services.AddService(typeof(Input), input);

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
font = Content.Load<SpriteFont>("font");

player1 = new Pad(this, new Vector2(0, 300), Vector2.Zero, 0);

player2 = new Pad(this, new Vector2(780, 300), Vector2.Zero, 1);

ball = new Ball(this, new Vector2(30, 330), new Vector2(5, 0));

base.Initialize();
}

Đầu tiên phải Update Input!


Kiểm tra va chạm của pad với ball, từ đó thay đổi vận tốc cho ball. Có nhiều cách để
làm việc này. Chúng ta tính khoảng cách từ trung tâm pad đến trung tâm ball để xác
định tốc độ trục Y cho ball. Có 2 lý do để sài thuật toán này.
+ Nó rất đơn giản chỉ là : Math.Abs(ball.Center.Y - player1.Center.Y)/5
+ Hiệu quả: khi người chơi đánh 1 pha bóng mạo hiểm (điểm chạm của quả bóng nằm
xa trung tâm của pad, do đó player dễ đánh hụt) họ sẽ làm cho vận tốc trục Y của ball
đặt giá trị cao (bóng sẽ nảy rất mạnh ) gây khó khăn cho đối thủ! Ngược lại thì 1 pha
bóng an toàn (điểm chạm gần tâm pad) sẽ đẩy bóng nảy yếu và đối thủ dễ phản công!
Cái này thể hiện 1 nguyên tắc rất cơ bản của game: Liều ăn nhiều 
protected override void Update(GameTime gameTime)
{
input.Update();

if (player1.CheckCollides(ball))
ball.Velocity = new Vector2(ball.Velocity.X * -1, Math.Abs(ball.Center.Y -
player1.Center.Y)/5);
if (player2.CheckCollides(ball))
ball.Velocity = new Vector2(ball.Velocity.X * -1, Math.Abs(ball.Center.Y -
player2.Center.Y)/5);
Sau khi bóng vượt qua biên màn hình, điểm sẽ được ghi và trái bóng được giao từ phía
người chơi vừa ghi điểm, code dưới đây thể hiện điều này:
if (ball.Position.X < 0)
{
player2.Point++;
ball.Position = new Vector2(740, 330);
ball.Velocity = new Vector2(-5, 0);
player1.Position = new Vector2(0, 300);
player2.Position = new Vector2(780, 300);
}
if(ball.Position.X > 760)
{
player1.Point++;
ball.Position = new Vector2(30, 330);
ball.Velocity = new Vector2(5, 0);
player1.Position = new Vector2(0, 300);
player2.Position = new Vector2(780, 300);
}
base.Update(gameTime);
}

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

Cuối cùng là Draw method. Bạn phải begin spriteBatch, sau đó, Draw cho các
GameComponent thông quá base.Draw(GameTime), cuối cùng dùng SpriteBatch vẽ
điểm số của từng người chơi lên màn hình
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
// Draw cho tất cả gameComponent có trong Components của Game1 !
// ball và pad điều đã add vào list này, xem lại lớp Sprite.cs
base.Draw(gameTime);

spriteBatch.DrawString(font, "Player 1: " + player1.Point, new Vector2(100,


100), Color.White);
spriteBatch.DrawString(font, "Player 2: " + player2.Point, new Vector2(600,
100), Color.White);

spriteBatch.End();
}
}
}
Về cơ bản thì Pong: player vs player đã hoàn thành, bạn chỉ cần xác định ai win kiểu
như:
if(player1.Point>4)
{
spriteBatch.DrawString(font, "Player 1 win!", new Vector2(200, 200), Color.White);
Components.Clear();
}
else if (player2.Point > 4)
{
Components.Clear();
spriteBatch.DrawString(font, "Player 2 win!", new Vector2(200, 200), Color.White);
}
Sẽ xác định người đầu tiên đạt 5 điểm sẽ dành chiến thắng!

Phần tiếp theo mình sẽ nói đến cách xây dựng AI cho Pong!

4. Xây dựng AIPad


4.1 Viết Class AIPad

Bạn xây dựng lớp AIPad kế thừa từ Sprite, chúng ta chỉ cần code như dưới đây, đơn
giản là AIPad sẽ có cách chuyển động khác với Pad mà người chơi điều khiển. Chúng
ta sẽ code AI tại class Game chính.
/// </summary>
public class AIPad : Sprite
{
int point = 0;

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
public int Point
{
get { return point; }
set { point = value; }
}
public AIPad(Game game, Vector2 pos, Vector2 velo)
: base(game, pos, velo)
{
texture = Game.Content.Load<Texture2D>("pad");
}
}

4.2 Sửa lại trong game chính như sau:

Đầu tiên tạo ra thể hiện cho AIPad:


AIPad player2;

Có 4 yếu tố tạo nên 1 AI tốt:


-Dễ bị đánh bại: Gamer nào cũng muốn win, nếu họ ko win đc, họ sẽ ko đời nào chơi
game của bạn.
-không dễ bị đánh bại: Đối thủ quá yếu làm gamer thấy chán và họ đi tìm một game
khó hơn.
-Khả năng thay đổi: Nó có thể từ dễ trở thành khó hơn khi người chơi lên tay và giữ họ
ngồi xuống lâu hơn để chinh phục các mức độ mới
-Khả năng sống động: AI cần có tính logic, có thể dự đoán đc giống như là một con
người đnag chơi cùng bạn vậy (di chuyển, tấn công...) giúp người chơi có thể phán
đoán và chiến thắng trong game.

Bây giờ nói đến cách tạo ra AI. Mấu chốt của game là làm sao cho pad đánh trúng (va
chạm) với ball để đánh quả banh về phía đối thủ phải hok nào. Giả sử ban code trong
Update như sau:
AIPad.Position.Y = Ball.Position.Y (Thực ra la hok dc vì Position là thuộc tính)
thì mọi chuyện sẽ ra sao? Hiển nhiên AIPad luôn cùng tọa độ Y với ball và do đó nó
luôn di chuyển cùng với ball => nó sẽ ko bao giờ đánh hụt. AI đã làm quá tốt, ko có 1
chút sơ hở nào và đây cũng là điểm ko tốt của AI. Người chơi ko bao giờ hứng thú với
AI Pad ko bao giờ đánh hụt. Nói cách khác là player sẽ ko bao giờ chiến thắng, họ sẽ
thấy điều này ko thú vị  . Do đó chúng ta sẽ code cho AIpad có tính "người" hơn.
Tức là nó vẫn có sơ hở để người chơi có thể chiến thắng

Theo cách tính vân tốc Y của ball: Math.Abs(ball.Center.Y - player1.Center.Y)/5

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

Trong file mẫu mình làm, quả bóng ball.texture.Width/2 = 20 pixel,


Pad.texture.Width/2 = 30 Pixel. Do đó khoảng cách xa nhất mà hai điểm center của
pad và ball có thể có mà vẫn đảm bảo va chạm giữa chúng là 50 pixel! Chia cho 5 tức
là vận tốc tối đa (theo độ lớn) mà ball có theo trục Y (ball.Velocity.Y) sẽ là 50/5 = 10
pixel / 1 lần Update . Vậy cú đánh tốt nhất của bạn có thể có sẽ có đổ nảy (tốc độ Y)
của trái banh là 10 pixel. Vậy nếu mình code như sau:
if (player2.Center.Y < ball.Center.Y)
{
player2.Velocity = new Vector2(0, 10);
}
else
{
player2.Velocity = new Vector2(0, -10);
}

Thì AI cũng ko bao giờ đánh hụt, đơn giản tốc độ Y của nó đã bằng với tốc độ Y cao
nhất có thể có là 10 pixel / 1 lần Update.

Để AIpad người hơn, chúng ta sửa lại

//Trong Update method !


player2 = new AIPad(this, new Vector2(780, 300), Vector2.Zero);

if (player2.Center.Y < ball.Center.Y)


{
player2.Velocity = new Vector2(0, 5);
}
else
{
player2.Velocity = new Vector2(0, -5);
}

Nếu như vậy với những cú đánh khó, AIpad sẽ bó tay chịu chết! nhưng với những cú
đánh dễ, nó sẽ phản đòn lại bạn và bạn sẽ loay hoay tìm cách chống đỡ! Sao nào, thú
vị đấy chứ, bây giờ bạn đã có 1 AI tạm ổn để chơi cùng! Để tăng độ khó, bạn tăng tốc
độ Y của AIpad từ 5 lên 9 xem nào 
Nâng cấp game: thử tăng số lượng pad cho nhiều người chơi hoặc chơi 1 lúc nhiểu
quả ball , tạo vật cản trên đường đi của ball ...

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

Chương 5: Các tạo gameComponent một Cách "Động" - Asteroids Project

Pong ở chương 4 là một game thú vị, phải ko nào? nhưng những thể hiện được chúng
ta tạo ra hoàn toàn bằng tay (khai báo, cấu trúc, điều khiển ... )

Thực tế trong game, object phải được tạo ra một cách "động", tùy thuộc điều kiện của
trò chơi. Ví dụ, 1 RangeMonster sẽ bắt ra 100 đạn lửa, đạn lửa sẽ phải đc tạo ra theo
cách động (chẳng lẽ bạn định khai báo 100 đạn lửa rùi khi nào cần thì Show nó ra ah)

Lần này mình sẽ hướng dẫn các bạn viết trò Asteroids. Một phi thuyền do người chơi
sẽ lơ lủng trong ko gian, phi thuyền bắn vở các thiên thạch trôi gần mình, khi vỡ sẽ có
nhiều mảnh thiên thạch nhỏ hơn vỡ ra, và cứ thế nếu cứ bắn , nó sẽ lại vỡ ra . Tất
nhiên tất cả thiên thạch sẽ được tạo ra 1 cách động! Ngoai ra trong game, minh sẽ
hướng dẫn cách tạo GUI đơn giản, các sử dụng particle effect

Chung ta cũng sẽ xóa bỏ thiên thạch bằng cách "đông" - dùng mã lệnh, khi chúng có
kích thước quá nhỏ!

Tài nguyên các bạn có thể sử dụng trong asteroids project mẫu mà mình làm (code và
debug hết gần 4h )

Hình dưới đây là toàn bộ nội dung dự án :

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

1.Core Folder

Chứa những class cơ sở: Sprite và GameScene


-Sprite mở rộng ra cho Player, Rock và bullet
-GameScene mở rộng ra cho StartScene, ActionScene và EndScene.
1.1Sprite
Sprite khá giống với sprite trong dự án pong, mình thêm vài thứ sau:

//Method loại bỏ sprite khi bạn ko cần dùng đến nó nữa (thiên thạch bị nổ chẳng hạn)
public virtual void Remove()
{
position = -100 * Vector2.One;
Visible = false;
Enabled = false;
Dispose(true);

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
//Trong update thêm code để sprite cuộn trong màn hình
public override void Update(GameTime gameTime)
{
position += velocity;
//Cuon trong man hinh
if (position.X < 0)
position.X = 800 - texture.Width;
if (position.X > 800 - texture.Width)
position.X = 0;
if (position.Y < 0)
position.Y = 600 - texture.Height;
if (position.Y > 600 - texture.Height)
position.Y = 0;
base.Update(gameTime);
}
Đầu tiên chúng ta nói qua về một Draw() method đầy đủ các tham số:
spriteBatch.Draw(texture, position , sourceRectangle,
Color.White, angle, origin, scale, SpriteEffects, depth);
Các tham số lần lượt là:
Texture: bức ảnh load từ Content và sẽ bao phủ lên toàn bộ sprite.
Position: Vị trí mà XNA sẽ Draw sprite lên màn hình của gamer
SourceRectangle: hình chữ nhật trên bức ảnh là chúng ta vừa load vào (chỉ có
những vùng trên bức ảnh thuộc hình chữ nhật đó mới đc thể hiện trong game. Bạn
có thể khởi tạo một Rectangle với thuộc tính tọa độ X,Y và chiều dài , chiều rộng.
Color: Màu sẽ tô nếu bức ảnh có vùng trống (tuy nhiên tô kiểu AlphaBlend thì nó sẽ
tự động không tô màu những vùng ảnh trống)
(float)angle: Góc sẽ xoay sprite (mặc định là 0f)
(Vector2)Origin : tọa độ điểm trung tâm sprite (đặt là tọa độ tâm của một frame),
Nếu angle != 0 thì sprite sẽ xoay xung quanh điểm origin.
Scale: Phóng to hay thu nhỏ sprite theo cùng tỉ lệ width/height
SpriteEffect: hiệu ứng lật texture (ngang, dọc hoặc ko dùng)
Depth: Độ sâu (sprite nào có depth cao hơn thì được draw chèn lên trên sprite có
detph thấp hơn, nếu cùng depth thì sprite nào đc tạo ra trước sẽ bị sprite Draw đè lên
trên (chỉnh thêm ở Begin() method của spriteBatch nữa... )
Trong Draw method, mình sử dụng sp.Draw với đầy đủ 7 tham số
public override void Draw(GameTime gameTime)
{
sp.Draw(texture, position,new Rectangle(0,0,texture.Width,texture.Height),
Color.White,angel, new Vector2(texture.Width/2,texture.Height/2)
,Scale,SpriteEffects.None,1f);
base.Draw(gameTime);
}

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
1.2.GameScene Class:
Bạn tạo ra 1 lớp mới thuôc loại DrawableGameCompoment, gọi là GameScene, có
thể đánh dấu nó là abtract cũng đc. Vì chúng ta sẽ tạo ra những class khác nhau cho
những màn khác nhau dựa trên sự thừa kế từ GameScene
Ban tạo những method sau:
Show() : Enable và Visiable của scêne là true, khi đó màn chơi sẽ đc hiển thị (đc
update và draw ở game chính)
Hide() : ngược với Show(), dùng khi bạn muốn qua màn khác

GameScene ko có mảng Compoments như của Game chính, dó đó bạn có thể tạo
mảng này (mảng chứa các gameCompoment khác trong màn, VD như là menu ở
màn start, Player và Enemy ở màn Action)

Compoments của GameScene cũng hoạt động giống như ở game chính vậy, do đó ở
Update và Draw method, bạn lần lượt Update và Draw cho mỗi object (kế thừa từ
GameCômpoment) có các thuộc tính Enable và Vísiable là true như sau:
public override void Update(GameTime gameTime)
{
// Update the child GameComponents
for (int i = 0; i < components.Count; i++)
{
if (components[i].Enabled)
{
components[i].Update(gameTime);
}
}

base.Update(gameTime);
}

/// <summary>
/// Allows the game component draw your content in game
screen
/// </summary>
public override void Draw(GameTime gameTime)
{
// Draw the child GameComponents (if drawable)
for (int i = 0; i < components.Count; i++)
{
GameComponent gc = components[i];

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
if ((gc is DrawableGameComponent) &&
((DrawableGameComponent) gc).Visible)
{
((DrawableGameComponent) gc).Draw(gameTime);
}
}
base.Draw(gameTime);
}
Dưới đây là toàn bộ code của GameScene:
#region Using Statements

using System.Collections.Generic;
using Microsoft.Xna.Framework;

#endregion

namespace game
{
/// <summary>
/// This is the base class for all game scenes.
/// </summary>
public abstract class GameScene : DrawableGameComponent
{
/// <summary>
/// List of child GameComponents
/// </summary>
private readonly List<GameComponent> components;

public GameScene(Game game)


: base(game)
{
components = new List<GameComponent>();
Visible = false;
Enabled = false;
}

/// <summary>
/// Show the scene
/// </summary>
public virtual void Show()
{
Visible = true;

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Enabled = true;
}

/// <summary>
/// Hide the scene
/// </summary>
public virtual void Hide()
{
Visible = false;
Enabled = false;
}

/// <summary>
/// Components of Game Scene
/// </summary>
public List<GameComponent> Components
{
get { return components; }
}

/// <summary>
/// Allows the game component to update itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing
values.</param>
public override void Update(GameTime gameTime)
{
// Update the child GameComponents
for (int i = 0; i < components.Count; i++)
{
if (components[i].Enabled)
{
components[i].Update(gameTime);
}
}

base.Update(gameTime);
}

/// <summary>
/// Allows the game component draw your content in game
screen

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
/// </summary>
public override void Draw(GameTime gameTime)
{
// Draw the child GameComponents (if drawable)
for (int i = 0; i < components.Count; i++)
{
GameComponent gc = components[i];
if ((gc is DrawableGameComponent) &&
((DrawableGameComponent) gc).Visible)
{
((DrawableGameComponent) gc).Draw(gameTime);
}
}
base.Draw(gameTime);
}
}
}

Sử dụng GameScene cũng đơn giản thôi, bạn tạo class cho Action Scêne chả hạn, khai
báo các thành phần của mảng này (Enemy, player thuộc loại Drawable) và add nó vào
mảng compoments của GameScene. Khi dùng trong game chính , vậy là khi show
chúng sẽ hoạt động và nhìn thấy, hide khi bạn chuyển màn (dựa vào thuộc tính Enable
và Visiable của GameScene) và Scene tự động Update và Draw (dựa vào thuộc tính
Enable và Visiable của chính thành phần trong mảng đó).

Cách1 đơn giản hơn rất nhiều đúng ko? Nhưng chỉ là với sl màn it thôi, màn nhiều thì
switch mệt nghỉ! Cách 2 thì phức tạp hơn nhưng tiện ở chỗ các compoments sẽ tự nó
Update và Draw, nếu game có nhiều nhân vật (VD: 100 Enemy khác nhau) thì cũng
tiết kiệm đc thời gian code đáng kể đấy! nếu dùng cách 2 khi chuyển màn trong game
chính , bạn dùng method sau để chuyển màn:
activeScene = anyScene;

protected void ShowScene(GameScene scene)


{
activeScene.Hide();
activeScene = scene;
scene.Show();
}

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Ở đây mình viết thêm hiệu ứng hình ảnh thông qua biến color, kỳ thuật đây là độ mờ
đục (tham số 4) của một new Color ! do thay đổi độ mờ đục của hình nền mà bạn sẽ
nhìn thấy như thể nó vừa hiện ra.
2. Effect folder

Nói chung thì đây là class tạo ra effect nổ, cháy thông qua các particle (texture rất
nhỏ), do chuyển động ko ngừng nên người xem có cảm giác có thứ gì đó đang phập
phùng, (nổ, cháy...)

Các class fire, smoke và explosion chứa các chỉ số cho phép bạn tạo ra effect khác
nhau, ... cũng khá đơn giản...

3. GUI Folder

Phần này mình viết mẫu các lớp Button (tạo ra nút chọn), Menu (gồm 1 danh sách
button và check keyboard để điều khiển button) và Bar (một thanh hiện thị HP của
player)

Cách sử dụng những lớp này xem thêm ở mã lệnh các Scene nhé.

4.Helper Folder

Chỉ bổ sung thêm lớp AssetPath, chứa các hằng số là đường đẫn đến resource trong
content để tiện khi code thui

5.Object Folder

Ở đây velocity của player được tính dụa trên thuộc tính góc (angel) và tốc độ (speed)
//KT phim di chuyen
if (input.Press(Keys.Up))
speed += 0.2f;
if (input.Press(Keys.Down))
speed -= 0.2f;
if (input.Press(Keys.Right))
angel += (float)Math.PI / 50;
if (input.Press(Keys.Left))
angel -= (float)Math.PI / 50;
speed = MathHelper.Clamp(speed, -3, 5);

velocity = new Vector2(speed * (float)Math.Cos(angel - MathHelper.PiOver2),


speed * (float)Math.Sin(angel - MathHelper.PiOver2));

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010

Mọi người xem bài "Lập trình ứng dụng toán và lý" tại mục xna cơ bản nhé
Hàm để tạo ra bullet một cách động:
//Check key de ban'
if (input.Press(Keys.Space))
{
if (time > 300)
{
time -= 300;
Bullet b = new Bullet(game, Position, Vector2.Zero,angel);
bullets.Add(b);
}
else
{
time += gameTime.ElapsedGameTime.Milliseconds;
}
}
else
{
time = 300;
}
Thực ra bullet cũng chuyển động dựa vào angle và speed như player, do bullet có
speed cao hơn (phần init của bullet) và có cùng angle với player => bullet sẽ bay cùng
hướng với player và có cảm giác phi thuyền đang bắn ra đạn!
Đây là đoạn mã Update của bullet:
public override void Update(GameTime gameTime)
{
velocity = new Vector2(speed * (float)Math.Cos(angel - MathHelper.PiOver2),
speed * (float)Math.Sin(angel - MathHelper.PiOver2));

//Neu co va cham, bullet tu tieu huy

position += velocity;

if (position.X < 0 || position.Y < 0 || position.X > 750 || position.Y > 550)
{
Remove();
}
}

Bullet ko cuộn mà sẽ bị remove khi ra khỏi biên màn hình!

6.Scene Folder

Đều kế thừa từ gameScene, mọi thể hiện đều tận dụng đc list
DrawableGameComponent của GameScene, thế nên những class nãy giờ mình viết,
mình đều add vào cái List này, (GUI, Sprites... ) rất tiện đúng ko?

6.1 SceneManager:
Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz
Copyrights by HuyetSat - Xvna.forumb.biz - 2010
public class SceneManager : Microsoft.Xna.Framework.DrawableGameComponent
{
GameScene active;
StartScene start;
ActionScene action;
HelpScene help;
EndScene end;
List<GameScene> sceneList = new List<GameScene>();

public List<GameScene> SceneList


{
get { return sceneList; }
}
internal EndScene End
{
get { return end; }
}
public GameScene Active
{
get { return active; }
}
internal HelpScene Help
{
get { return help; }
}
internal StartScene Start
{
get { return start; }
}
public ActionScene Action
{
get { return action; }
set { action = value; }
}

public SceneManager(Asteroids game)


: base(game)
{
start = new StartScene(game, Game.Content.Load<Texture2D>(AssetPath.BG +
"startBG"));
action = new ActionScene(game, Game.Content.Load<Texture2D>(AssetPath.BG +
"battleBG"));
help = new HelpScene(game, Game.Content.Load<Texture2D>(AssetPath.BG +
"startBG"));
end = new EndScene(game, Game.Content.Load<Texture2D>(AssetPath.BG + "startBG"));

sceneList.Add(start);
sceneList.Add(action);
sceneList.Add(help);
sceneList.Add(end);

active = start;
ShowScene(start);
}

/// <summary>
/// Allows the game component to perform any initialization it needs to before
starting

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
/// to run. This is where it can query for any required services and load content.
/// </summary>
public override void Initialize()
{
foreach (GameScene scene in sceneList)
scene.Initialize();

base.Initialize();
}
Ở đây, đầu tiên tạo ra một danh sách các màn (Scene) trong game, từ đó mình khai
báo, thiết lập các màn và Add nó vào líst. Ngoài ra khai báo thêm 1 ActiveScene nhằm
mục đích:
+bạn muốn chạy scene nào thì gán nó thành ActiveScene thông qua ShowScene
method !
+Quá trình Update và Draw Scene, nó chỉ thực hiện với ActiveScene, do đó bạn sẽ tốn
ít tài nguyên máy tính khi phải lặp đi lặp lại mã lệnh trong Update và Draw !
public void ShowScene(GameScene scene)
{
active.Hide();
scene.Initialize();
active = scene;
scene.Show();
}
/// <summary>
/// Allows the game component to update itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
public override void Update(GameTime gameTime)
{
active.Update(gameTime);

base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{

active.Draw(gameTime);

base.Draw(gameTime);
}
}
}
Quá đơn giản đúng ko nào 

6.2 StartScene, EndScene.

Thêm một vài button, add nó vào compoments...

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
Cái chính là biến game ở đây, tham số thuộc kiểu Asteroids , tức chính là class Game
chính của chúng ta. Mình tạo 1 thuộc tính SceneManager trả về biến sceneManager
của game chính, do đó từ những Scene phụ như Start, End, bạn cũng có thể điều khiển
việc chuyển màn thông qua SceneManager !
class StartScene:GameScene
{

Button start;
Button end;

public StartScene(Asteroids game, Texture2D BG)


: base(game, BG)
{
Compoments = new List<DrawableGameComponent>();
Texture2D active = game.Content.Load<Texture2D>(AssetPath.GUI+"Selected");
Texture2D wait = game.Content.Load<Texture2D>(AssetPath.GUI + "NoSelect");
start = new Button(game, active, wait, Button.Status.active,"Start!");
start.Position = Vector2.Zero;
end = new Button(game, active, wait, Button.Status.wait,"Quit");
end.Position = new Vector2(0, 50);
listButton.Add(start);
listButton.Add(end);
menu = new Menu(game, listButton);
Compoments.Add(menu);
input = (Input)Game.Services.GetService(typeof(Input)) as Input;
}
public override void Update(GameTime gameTime)
{
if (menu.Index == 0 && input.Release(Keys.Enter))
{
game.SceneManager.Action = new ActionScene(game,
game.Content.Load<Texture2D>(AssetPath.BG + "battleBG"));
game.SceneManager.ShowScene(game.SceneManager.Action);
}
if (menu.Index == 1 && input.Release(Keys.Enter))
Game.Exit();
base.Update(gameTime);
}
}

6.3 ActionScene

Để update và check va chạm cho các object trong game (rock, player, bullet), xem mã
sau:
for (int i = 0; i < rocks.Count;i++ )
{

foreach (Bullet b in player.Bullets)


{
//KT va cham bullet voi rock
if (b.CheckCollides(rocks[i]))
{
b.Remove();
explosion.AddParticles(rocks[i].Center);

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
smoke.AddParticles(rocks[i].Center);
player.Point += 100;
//Tao them 3 rock nho? hon
if (rocks[i].Scale > 0.9f)
{
Rock r1 = new Rock(game, rocks[i].Center, new
Vector2(GetRanDom.Get(5) - 2, GetRanDom.Get(5) - 2), rocks[i].Scale * 2 / 3);
rocks.Add(r1);
Compoments.Add(r1);
Rock r2 = new Rock(game, rocks[i].Center, new
Vector2(GetRanDom.Get(5) - 2, GetRanDom.Get(5) - 2), rocks[i].Scale * 2 / 3);
rocks.Add(r2);
Compoments.Add(r2);
Rock r3 = new Rock(game, rocks[i].Center, new
Vector2(GetRanDom.Get(5) - 2, GetRanDom.Get(5) - 2), rocks[i].Scale * 2 / 3);
rocks.Add(r3);
Compoments.Add(r3);
}
rocks[i].Remove();
}
//Update cho bullet

}
//KT va cham cua player voi rock
if (player.CheckCollides(rocks[i]))
{
fire.AddParticles(player.Position);
smoke.AddParticles(player.Position);
rocks[i].Remove();
player.HitPoint--;
if (player.HitPoint <= 0)
{
score = player.Point;
game.SceneManager.ShowScene(game.SceneManager.End);
}
}
Class cho từng object chỉ nên code những vấn đề của riêng object đó, còn khi những
object tương tác với nhau, tốt hơn bạn nên code trong Scene chung của các object, ở
đây là ActionScene. Dùng lệnh for và foreach để KT check va chạm giữa các object,
ngoài ra còn remove những object ko còn sử dụng đã được gọi phương thức Remove()
Việc remove object khoi list object (bullets, rocks) và component của GameScene sẽ
làm cho số lần lặp mã lệnh mỗi đợt Update và Draw giảm xuống, game cũng sẽ chạy
mượt hơn (trò này ít chứ mấy game chiến thuật thì có cả ngàn object)
if (!rocks[i].Enabled)
{
Compoments.Remove(rocks[i]);
rocks.Remove(rocks[i]);
}

for (int i = 0; i < player.Bullets.Count;i++)


if (player.Bullets[i].Enabled)
player.Bullets[i].Update(gameTime);
else

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
player.Bullets.Remove(player.Bullets[i]));

Action có 1 biến lưu lại số điểm người chơi - score.


score = player.Point;

(mỗi lần bắn trúng rock, score của player sẽ tăng), khi player chết (HP <0), score sẽ
đưa ra thành tích người chơi ở EndScene.

7. Class game chính - Asteroids.cs


Game chính hầu như chả có gì ngoài vài cái service truyền cho GameComponent
(Input, spriteBatch), ngoài ra bạn tạo 1 thể hiện và cấu trúc cho sceneManager, rùi add
nó vào Component, thế thui, mọi chuyện h tự cái method base.Update(gameTime) với
base.Draw(GameTime) nó tự làm việc, viết theo kiểu này bạn đã tận dụng được tốt đa
sức mạnh của xna! Mã lệnh game chính này chỉ chạy chủ yếu dựa vào kế thừa từ
Microsoft.Xna.Framework.Game
public class Asteroids : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;

SceneManager sceneManager;

//Services:
Input input = new Input();
SpriteBatch spriteBatch;

public SceneManager SceneManager


{
get { return sceneManager; }
}

public Asteroids()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";

graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
}
protected override void Initialize()
{
spriteBatch = new SpriteBatch(GraphicsDevice);

//Add services
Services.AddService(typeof(SpriteBatch), spriteBatch);
Services.AddService(typeof(Input), input);

sceneManager = new SceneManager(this);


sceneManager.Initialize();
Components.Add(sceneManager);

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz


Copyrights by HuyetSat - Xvna.forumb.biz - 2010
base.Initialize();
}

protected override void Update(GameTime gameTime)


{
input.Update();
//Update Active Scene !
base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)


{
GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
//Draw Active Scene !
base.Draw(gameTime);
spriteBatch.End();
}
}
Lẽ ra cái code dưới này xna tạo sẵn cái program.cs cho bạn, mình gom nó vào class
game chính cho gọn lại thui
//Entry point! - WRITTEN BY HUYETSAT - Xvna.forumb.biz :)
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
using (Asteroids game = new Asteroids())
{
game.Run();
}
}
}
}
Xong Asteroids rùi đó, mong các bạn tự tìm lại cách viết thông qua hướng dẫn mình
nhé, nếu thấy có thì mở file mẫu ra xem rùi nhớ tự code chứ đùng C với V ở đây nhá

Nâng cấp game:


Có rất nhiều thứ để thử khả năng lập trình của bạn: tăng thêm Item, thêm Ênemy có
AI, viết thêm thằng Boss, thêm vũ khí như tên lửa, vòng bảo vệ ... Ai phát triển tiếp trò
asteroid này thì nhớ Up lên để mọi người cùng thưởng thức nhé 

Lời kết: Hi vọng các newbie sẽ thấy thích thú với game coding thông qua tài liệu này!
Mình cũng mất gần 2 ngày để viết tài liệu + làm Resource + tạo project mẫu!

Chúc mọi người training Zui Zẻ - HuyetSat - Xvna.forumb.biz

Contact me at thanh_vinh648@yahoo.com or http://xvna.forumb.biz

You might also like