Professional Documents
Culture Documents
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.
==========================================
+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!
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.
4. Giao diện sử dụng cho coder khi bạn làm việc trên Visual C# 2008 express
edition!
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) ...
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!
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...
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.
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)
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)
Đâ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
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 :)
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
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();
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();
}
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);
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);
}
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
Chúng ta sẽ xây dựng 2 phiên bản cho pong: player vs player và player vs AI !
: Cây vợt
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 đá...).
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));
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);
}
}
}
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);
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);
}
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);
base.Initialize();
}
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);
}
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.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!
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;
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
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.
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 ...
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 )
1.Core Folder
//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);
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];
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;
/// <summary>
/// Show the scene
/// </summary>
public virtual void Show()
{
Visible = 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
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;
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);
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));
position += velocity;
if (position.X < 0 || position.Y < 0 || position.X > 750 || position.Y > 550)
{
Remove();
}
}
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>();
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
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
Button start;
Button end;
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++ )
{
}
//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]);
}
(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.
SceneManager sceneManager;
//Services:
Input input = new Input();
SpriteBatch spriteBatch;
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);
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á
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!