Bài được trích từ trang caulacbovb.com , tuy không mới nhưng khá vất vả cho người mới lập trình
Giới thiệu DirectX
DirectX
là một bộ các thành phần được thiết kế để giúp lập trình viên có thể
phát triển ứng dụng đa phương tiện trên nền Windows như game hay một số
ứng dụng khác sử dụng đồ họa 2D, 3D.
Phiên bản DirectX mới nhất hiện
giờ là phiên bản 10, nhưng yêu cầu cấu hình rất cao và chỉ được hỗ trợ
trên Windows Vista, phiên bản phổ biến hiện tại là DirectX9, với
DirectX9, bạn có thể dễ dàng phát triển ứng dụng 2D, 3D một cách nhanh
chóng.
Những ngôn ngữ hỗ trợ DirectX9 gồm (trong bộ Visual Studio):
- Microsoft Visual C#
- Microsoft Visual Basic.NET
- Microsoft Visual C++
Bộ
thư viện lập trình DirectX được cung cấp bằng các gói DirectX SDK
(DirectX Software Development Kit), bạn có thể tải DirectX SDK trên
trang của Microsoft.
DirectX 9 bao gồm các thành phần:
DirectX Graphics
- DirectDraw: Quản lý đồ họa 2D
- Direct3D: Quản lý đồ họa 3D
Direct Input: Quản lý các đối tượng điều khiển (bàn phím, chuột, joystick,...)
Direct Music: Quản lý âm thanh, nhạc (nhạc nền,...)
Direct Play: Quản lý mạng (để thực hiện các thao tác truyền tải dữ liệu qua mạng, như game online)
DirectX 9 còn gọi là Managed DirectX (MDX) vì nó được xây dựng cho các ngôn ngữ "Managed" (như VB.NET, C#)
Tạo 1 Windows Application mới, trong cửa sổ Solution Explorer, click phải chuột chọn Add Reference
Tìm xem có thấy một lô những thư viện có : Microsoft.DirectX... không
Thường thì sẽ có
còn rủi ko có thì bạn phải cài lại DxSDK thôi
Cơ bản nhất trong các thành phần của DirectX9: DirectDraw
Ta sử dụng DirectDraw (DD) để lập trình 2D.
Bắt
đầu, bạn tạo 1 windows application mới, rồi add reference 2 cái thư
viện Microsoft.DirectX và Microsoft.DirectX.DirectDraw vào
add xong thì cửa sổ Solution explorer nó sẽ ra thế này
rồi, vào code của file Form1.vb, gõ vô:
'Báo cáo cho chương trình biết là ta sẽ dùng 2 thư viện này
Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectDraw
Ta cần thêm một số biến để quản lý cho DD
Public dddevice As Device = Nothing 'Đối tượng Device cho DD
Public psurface As Surface ' 2 cái surface (bề mặt)
Public ssurface As Surface '
Private clip As Clipper = Nothing
Cái Device là để
cho DD, 2 cái surface, 1 cái là primary surface và 1 cái là secondary
surface. Primary surface là surface nằm trên, cái secondary nằm dưới
(giống như 2 miếng giấy chồng lên nhau), những gì ta vẽ lên ssurface sẽ
được hiển thị lên psurface để người sử dụng thấy.
Bây giờ vào Project properties, đặt Startup là Sub Main
Public Shared Sub Main()
Dim GameForm As New form1( ) ' Đối tượng dùng để vẽ sẽ là Form1
Application.Exit( ) ' Tắt
End Sub
Tại sub main, chúng ta khởi tạo 1 đối tượng của
Form1 tên là GameForm, form này sẽ dùng để hiển thị tất cả những gì ta
vẽ (nói chung nó là cửa sổ game
)
Trong hàm dựng của class form1
Public Sub New()
MyBase.New() ' Gọi hàm dựng của base class
InitializeComponent()
Me.Show()
'Khởi tạo device
dddevice = Nothing
dddevice = New Device()
dddevice.SetCooperativeLevel(Me, CooperativeLevelFlags.FullscreenExclusive)
Init()
Gameloop()
End Sub
Hàm Init để khởi tạo môi trường DirectX và gán môi
trường đó vào Form1, hàm Gameloop() là MainLoop (vòng lặp chính - sẽ nói
sau) của game.
Public Sub Init( )
psurface = Nothing
ssurface = Nothing
Dim sdesc As New SurfaceDescription()
clip = New Clipper(dddevice)
clip.Window = Me
sdesc.SurfaceCaps.PrimarySurface = True
sdesc.SurfaceCaps.Flip = True
sdesc.SurfaceCaps.Complex = True
sdesc.BackBufferCount = 1
psurface = New Surface(sdesc, dddevice)
psurface.Clipper = clip
sdesc.Clear()
sdesc.SurfaceCaps.BackBuffer = True
ssurface = psurface.GetAttachedSurface(sdesc.SurfaceCaps)
End Sub
Private Sub Gameloop( )
Do While Me.Created
'--------------Thực hiện lệnh----------------
If Not Me.Focused Then
Application.DoEvents( )
Else
ShowSurface( )
Application.DoEvents( )
End If
'--------------------------------------------
Loop
ending( )
End Sub
MainLoop có nhiệm vụ là: thực hiện lặp lại khối lệnh nằm
trong khu "Thực hiện lệnh" liên tục cho đến khi nào có điều kiện để kết
thúc thì dừng.
Thủ tục ShowSurface để vẽ lại surface
Public Sub ShowSurface()
ssurface.ColorFill(Color.White) 'Xóa bề mặt vừa vẽ (thực chất là tô màu trắng toàn bộ)
psurface.Flip(ssurface, FlipFlags.Wait) 'lật ssurface lên trên
End Sub
Thủ tục Ending được gọi khi kết thúc mainloop, có nhiệm vụ hủy hết tất cả các đối tượng được tạo ở trên
Public Sub ending()
dddevice.Dispose()
psurface.Dispose()
ssurface.Dispose()
clip.Dispose()
End Sub
Bây giờ bạn có thể chạy thử chương trình, nếu muốn tắt
chương trình bằng phim ESC (như các game thường thấy), bạn viết code cho
sự kiện Key_Down của form
Private Sub form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.Escape Then
ending() 'Khi gặp phím ESC thì gọi thủ tục ending rồi tắt luôn
End
End If
End Sub
Ở
bài viết trên, ta đã tìm hiểu về surface, có một vấn đề ở đây. Đó là
khi bạn minimize cửa sổ game rồi đưa lên lại, có thể nó sẽ đen thui, ko
thấy gì nữa
, trong phần này, ta sẽ khắc phục vấn đề này.
Trong phần chương trình ở bài trước, ta sẽ thay đổi 1 tí và thêm 1 thủ tục GFocus
Thủ tục Init()
Public Sub New( )
MyBase.New( )
InitializeComponent( )
Me.Show( )
AddHandler Me.GotFocus, AddressOf GFocus ' Thêm cái này
dddevice = Nothing
dddevice = New Device( )
dddevice.SetCooperativeLevel(Me, CooperativeLevelFlags.FullscreenExclusive)
Init()
GameLoop()
End Sub
Dòng lệnh vừa thêm vào:
AddHandler Me.GotFocus, AddressOf GFocus
để điều khiển sự kiện GotFocus của form, khi form mất focus, các
surface sẽ mất, sau đó khi form lại nhận focus thì sẽ thực hiện thủ tục
GFocus
Public Sub GFocus(ByVal sender As Object, ByVal e As System.EventArgs)
ending() 'Huỷ cái form cũ
Dim newform As New Form1() 'tạo lại form mới
End Sub
Sau khi được nhận focus mới, form cũ sẽ bị huỷ và tạo lại 1 form mới.
Sửa trong ShowSurface() tí
Public Sub ShowSurface()
Try
ssurface.ColorFill(Color.White)
psurface.Flip(ssurface, FlipFlags.Wait)
Catch e As Exception
Init()
End Try
End Sub
Thêm khối try...end try vì khi tạo lại form, hàm init có thể sẽ sinh lỗi.
Rồi, giờ bạn chạy thử xem, thử thu cửa sổ game xuống taskbar rồi gọi nó lên lại xem
giờ thì không bị lỗi như trước nữa
Load hình vào surface
Bây
giờ ta sẽ tìm hiểu cách để vẽ một hình ảnh lên màn hình bằng cách khởi
tạo 1 surface, trên surface đó đã được gắng cái hình cần vẽ lên
Trong phần khai báo ssurface, psurface, ddevice, bạn thêm vào 1 cái surface nữa tên charsurface
Dim charsurface As Surface
Thủ tục Init thêm vào đoạn :
sdesc.Clear()
sdesc.SurfaceCaps.OffScreenPlain = True
charsurface = New Surface("gametitle.bmp", sdesc, dddevice)
Public Sub Init( )
psurface = Nothing
ssurface = Nothing
Dim sdesc As New SurfaceDescription()
clip = New Clipper(dddevice)
clip.Window = Me
sdesc.SurfaceCaps.PrimarySurface = True
sdesc.SurfaceCaps.Flip = True
sdesc.SurfaceCaps.Complex = True
sdesc.BackBufferCount = 1
psurface = New Surface(sdesc, dddevice)
psurface.Clipper = clip
sdesc.Clear()
sdesc.SurfaceCaps.BackBuffer = True
ssurface = psurface.GetAttachedSurface(sdesc.SurfaceCaps)
sdesc.Clear()
sdesc.SurfaceCaps.OffScreenPlain = True
charsurface = New Surface("gametitle.bmp", sdesc, dddevice) 'Load file gametitle.bmp vào charsurface
End Sub
Đoạn code thêm vào có nhiệm vụ, xoá sdesc đi, lấy file
gametitle.bmp từ trên đĩa cứng "dán" vào charsurface và đưa charsurface
lên sdesc.
(sdesc là đối tượng quản lý các surface)
Trong thủ tục ShowSurface(), ta sẽ vẽ những thứ ta cần tại đây
Public Sub ShowSurface()
Try
ssurface.ColorFill(Color.White)
ssurface.DrawFast(150, 150, charsurface, DrawFastFlags.DoNotWait) 'vẽ charsurface ra
ssurface.ForeColor = Color.Black
ssurface.DrawText(540, 565, "CaulacboVB", False) 'In lên màn hình dòng chữ CaulacboVB ở toạ độ (540,565), chữ màu đen
psurface.Flip(ssurface, FlipFlags.Wait)
Catch ex As SurfaceLostException
Init()
End Try
End Sub
Parsed in 0.000 seconds, using
GeSHi 1.0.8.11
Thu Mar 27, 2008 7:05 am
Diễn hoạt (Animation)
Cái
chữ animation là làm cho hình ảnh chuyển động, nôm na là thế, dịch ra
tiếng việt nên chọn chữ diễn hoạt cho nó gọn, nhưng chữ đó hơi "xấu"
Trong bài trên, ta đã biết cách vẽ một hình ảnh ra màn hình, trong bài này, ta sẽ vẽ một hình ảnh chuyển động lên màn hình.
Ở đoạn load hình trong thủ tục Init, bạn sửa lại tên file là: character.bmp
image8.gif (bạn phải dùng paint hoặc photoshop lưu file này lại thành character.bmp - DirectDraw dùng file BMP)
File character.bmp này, bạn thấy nó có nhiều hình giống nhau mô tả việc di chuyển của 1 thằng cha có râu
,
ta sẽ load hình này và vẽ từng khúc (frame) của nó, mỗi khúc có kích
thước là 141x250 (file này tương đương 4 khúc của hoạt động)
Mỗi khúc vẽ ra sẽ như thế này
Thay
đổi vị trí vẽ liên tục trong 1 khoảng thời gian ngắng thì sẽ có cảm
giác là thằng này đang bước đi thật (cái này thì ai cũng biết, kĩ thuật
hoạt hình cơ bản)
Để xác định được 4 khúc của hình, ta dùng đối tượng Rectangle
Private frames(4) As Rectangle
Private charsurface As Surface
Private frm4act As Integer = 0
Viết thủ tục createFrame để xác định vùng nào cần vẽ của frame nào
Public Sub createframes()
Dim x As Integer
For x = 0 To 3
frames(x) = New Rectangle(141 * x, 0, 141, 250)
Next
End Sub
đối tượng Rectangle có tham số khởi tạo là (x, y,
width, height), mỗi frame ta sẽ vẽ ra có kích thước 141x250 tương đương
width=141, height=250, vị trí x=141*x để mỗi frame, chương trình sẽ lấy
vị trí khớp cho nó, y=0 là vì, nếu ta lấy giá trị y khác 0, vùng được vẽ
ra (viền đỏ) sẽ bị cắt hình.
y=0
y khác 0
Giờ ta sẽ vẽ nó ra, sửa thủ tục ShowSurface thành thế này:
Public Sub ShowSurface()
Try
ssurface.ColorFill(Color.Red)
ssurface.DrawFast(100, 300, charsurface, frames(frm4act), DrawFastFlags.DoNotWait)
Catch ex As SurfaceLostException
Init()
End Try
psurface.Flip(ssurface, FlipFlags.Wait)
End Sub
Vẽ charsurface ra với kích thước của frames(frm4act), vị trí 100:300
frm4act
là 1 cái biến thôi, nó cho biết frame hiện tại là frame nào, bây giờ
nếu chạy thì ta sẽ chưa thấy nó chuyển động, mục đích của ta là khi nhấn
phím mũi tên, thằng râu này nó mới hoạt động, bằng cách tăng giảm giá
trị của frm4act.
Private Sub form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.Right Then
frm4act = frm4act + 1
if frm4act > 3 then 'Nếu frm4act vượt quá 3 thì
frm4act = 0 ' cho nó quay về 0
End If
ShowSurface( )
End if
If e.KeyCode = Keys.Escape Then
ending()
End
End If
End Sub
Giờ test nào, khi nhấn phím mũi tên sang phải, bạn sẽ thấy
thằng râu chạy (chỉ hoạt hình thôi), nhưng nó chạy mà ko lết tới trước
đc tí nào cả
, nếu muốn nó chạy tới, bạn thêm 1 biến x4act để lưu toạ độ x cho nó.
Private x4act As Integer = 100
lệnh vẽ ở thủ tục Showsurface cũng thay đổi như thế này
ssurface.DrawFast(x4act, 300, charsurface, frames(frm4act), DrawFastFlags.DoNotWait)
Private Sub form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.Right Then
frm4act = frm4act + 1
x4act += 5 ' cho thằng râu chạy tới (thay đổi toạ độ x của nó)
if frm4act > 3 then
frm4act = 0
End If
End If
ShowSurface( )
End if
If e.KeyCode = Keys.Escape Then
ending()
End
End If
End Sub
Giờ chạy thử xem, thằng râu đã chạy được rồi đấy
Nhưng vẫn còn cái nền màu xám khó chịu, ở bài sau, ta sẽ tìm hiểu cách bỏ cái nền đó.
Trong suốt
Ở
những bài trước, ta đã biết cách load và vẽ một hình từ file lên màn
hình, hoạt hình, tuy nhiên những hình ảnh vẫn bị dính cái nền xám, bài
này ta sẽ tìm cách xử chúng
Nguyên
tắc để bỏ cái màu nền xám kia đi là ta đặt thuộc tính ColorKey cho nó,
khi ta vẽ một ảnh lên màn hình, với thuộc tính ColorKey để chỉ định màu
của cái nền, thì khi vẽ ra, máy sẽ xử lý bỏ cái nền đó đi.
Giờ ta sẽ khai báo một biến làm ColorKey
- Code:
Thủ tục Init bây giờ sửa lại:
- Code:
-
- Public Sub Init( )
-
- psurface = Nothing
- ssurface = Nothing
- Dim sdesc As New SurfaceDescription()
- clip = New Clipper(dddevice)
- clip.Window = targetform
- sdesc.SurfaceCaps.PrimarySurface = True
- sdesc.SurfaceCaps.Flip = True
- sdesc.SurfaceCaps.Complex = True
- sdesc.BackBufferCount = 1
- psurface = New Surface(sdesc, dddevice)
- psurface.Clipper = clip
- sdesc.Clear()
- sdesc.SurfaceCaps.BackBuffer = True
- ssurface = psurface.GetAttachedSurface(sdesc.SurfaceCaps)
- sdesc.Clear()
- sdesc.SurfaceCaps.OffScreenPlain = True
- ck.ColorSpaceHighValue = RGB(153, 153, 153)
- ck.ColorSpaceLowValue = RGB(153, 153, 153)
- charsurface = New Surface("character.bmp", sdesc, dddevice) 'initialize new surface
- charsurface.SetColorKey(ColorKeyFlags.SourceDraw, ck) 'set the color key for the surface
-
- End Sub
-
không có gì đặc biệt, chỉ có khúc này:
- Code:
-
- ck.ColorSpaceHighValue = RGB(153, 153, 153)
- ck.ColorSpaceLowValue = RGB(153, 153, 153)
-
ColorSpaceHighValue
và ColorSpaceLowValue, 2 giá trị này nếu khác nhau thì ảnh sẽ mờ mờ
trong trong kiểu như gương, đây là khoảng trong suốt mà ta có thể tùy
biến, ở đây ta muốn làm mất luôn cái nền màu xám, vì thế nên ta cho 2
giá trị giống nhau, đều là (153, 153, 153) (đây là mã màu của màu xám
trong hệ RGB)
Sau đó ta gán thuộc tính ColorKey của Surface với ck.
- Code:
- charsurface.SetColorKey(ColorKeyFlags.SourceDraw, ck)
Bây
giờ, ColorKey đã được gán vào cho surface, nhưng vẽ ra thì vẫn chưa
thấy mất cái nền, đó là vì ở dòng lệnh vẽ hình, ta vẫn chưa cho nó biết
là ta vẽ cái hình ra theo colorKey, bạn hãy sửa thủ tục ShowSurface()
thành thế này:
- Code:
-
- Public Sub ShowSurface()
- Try
- ssurface.ColorFill(Color.Red)
-
- ssurface.DrawFast(x4act, 100, charsurface, frames(frm4act), DrawFastFlags.SourceColorKey) 'Vẽ ra, giờ có thêm tham số DrawFastFlags.SourceColorKey tức là vẽ có xài ColorKey
-
- Catch ex As SurfaceLostException
- Init()
- End Try
-
- psurface.Flip(ssurface, FlipFlags.Wait)
- End Sub
-
Xong! giờ bạn chạy thử xem nào.
Hình nền
Trong
phần này, ta sẽ vẽ một hình nền (background) nằm ở phía sau "thằng râu"
ở các bài trên, khi "thằng râu" bước đi thì cái hình nền cũng sẽ trượt
theo, tạo cảm giác là nó đang bước đi thật (giống kiểu của game mario -
gọi là platform game)
Đầu tiên, ta cần khởi tạo thêm:
- Code:
-
- Private backrect As Rectangle
- Private backsurface As Surface = Nothing
-
- Private xaxis as Integer
-
giống
như khi vẽ "thằng râu", bạn sẽ có một rectangle làm giới hạn cho khung
nhìn, một surface để đưa hình làm nền vào và một biến xaxis để lưu vị
trí của nền.
Sau đó ta viết thủ tục Background() để khởi tạo background
- Code:
-
- Private Sub Background()
- Dim sdesc As New SurfaceDescription()
- sdesc.SurfaceCaps.OffScreenPlain = True
- backsurface = New Surface("background.bmp", sdesc, dddevice)
- backrect.Size = New Size(800, 600)
- End Sub
-
và gọi thủ tục Background() ở cuối thủ tục Init()
Trong
đoạn code trên, ta khởi tạo một surface để chứa hình nền, hình sẽ được
load từ file background.bmp (có trong cùng thư mục chứa mã nguồn, hoặc
bạn có thể download ở đây)
Background.rar
và khởi tạo một rectangle có kích thước 800x600 (kích thước màn hình game), khi vẽ ta sẽ chỉ vẽ một khung chừng đó thôi.
[Còn tiếp...]
You do not have the required permissions to view the files attached to this post.
Sat May 24, 2008 10:24 am
Anh cho em hỏi mấy câu sau:
1. Khi khai báo clip:
- Code:
- Private clip As Clipper = Nothing
rồi sử dụng trong:
- Code:
- clip = New Clipper(dddevice)
- clip.Window = Me
- 'some code
- psurface.Clipper = clip
-
Có tác dụng gì?
2. Dòng lệnh:
- Code:
- sdesc.SurfaceCaps.PrimarySurface = True
- sdesc.SurfaceCaps.Flip = True
- sdesc.SurfaceCaps.Complex = True
và
- Code:
- sdesc.SurfaceCaps.BackBuffer = True
- ssurface = psurface.GetAttachedSurface(sdesc.SurfaceCaps)
Có công dụng gì?
3. Cái DrawFastFlags.DoNotWait để làm gì? Vẽ không cần đợi à?
4. Làm thế nào để vẽ trong chế độ Window?
Cảm ơn!
4. Vẽ trong chế độ window? chẳng phải là đang vẽ lên form đấy sao?
1.
Clipper có tác dụng giữ lại sprite khi nó ra khỏi phạm vi màn hình,
DirectX7 không hỗ trợ chức năng này nên khi chỉ 1 phần của sprite ra
khỏi phạm vi màn hình nó sẽ biến mất, clip có tác dụng giữ nó lại.
2.
sdesc (SurfaceDecription) tạm hiểu là đối tượng định nghĩa/định hình
cho surface, ở dòng lệnh trên, ta "nói" cho chương trình biết:
sdesc.SurfaceCaps.PrimarySurface = True => kích hoạt primary surface
sdesc.SurfaceCaps.Flip = True => kích hoạt tính năng flip (khả năng lật qua lật lại giữa các surface)
sdesc.SurfaceCaps.Complex
= True => định nghĩa kiểu "surface phức tạp", các surface con có thể
gắng vào surface ban đầu nhờ kiểu này
sdesc.SurfaceCaps.BackBuffer = True => bộ đệm cho surface, giúp cho việc vẽ/flip ổn định hơn
ssurface = psurface.GetAttachedSurface(sdesc.SurfaceCaps) => ssurface là surface con của psurface
3. Thực ra đây là một tham số khi vẽ bằng DirectDraw, ít có tài liệu nói rõ về vấn đề này (hoặc có thể là mình tìm ko ra
), theo thực nghiệm bạn sẽ hiểu hơn về các thông số này, chất lượng hình ảnh tùy thuộc vào các thông số này.