Tải bản đầy đủ (.pdf) (9 trang)

Trò chơi xếp hình với VB6

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

Trò chơi xếp hình với VB6

Chương trình sẽ cắt một hình cho trước ra thành nhiều mảnh có hình dạng ngẫu nhiên, và
người chơi sẽ phải ráp từng mảnh lại. Trò chơi thật đơn giản nhưng lập trình để tạo trò chơi
này không đơn giản chút nào. Bài viết giới thiệu một chương trình như vậy được thực hiện với
VB6.
Tạo Activex control

Tạo một Standard Project mới. Vào menu Project/Add Usercontrol để thêm một Usercontrol mới
(đặt tên tùy thích, ở đây tôi đặt là ShapeControl, AutoRedraw=True). Để tạo ShapeControl (SC)
có hình dạng đặc biệt cần dùng 4 hàm API: CreateRectRgn, CreateEllipseticRgn, CombineRgn
và SetWindowRgn. Khai báo các hàm trên trong SC. Khai báo thêm hàm DeleteObject dùng để
hủy đối tượng đã tạo để giải phóng bộ nhớ.
Để thuận tiện cho việc "tạo hình" cho SC, ta sử dụng cấu trúc để lưu trữ dữ liệu các cạnh:
Private Type CauTruc
Top As Long
Bottom As Long
Left As Long
Right As Long
End Type
Function sau tạo hình cho SC:
Private Function CreateFormRegion(ScaleX As Single, ScaleY
As Single, OffsetX As Integer, OffsetY As Integer, DrawStyle
As CauTruc) As Long
Hình chữ nhật chính có tọa độ (22,22)-(77,77), hình ellipse có
bán kính lớn=22 và bán kính nhỏ=13 (H.1). Do hình ảnh chúng
ta muốn cắt có chiều rộng và chiều dài bất kì nên ta phải nhân
tỉ lệ này cho chiều dài, rộng thực của mỗi miếng hình nhỏ
(bằng với chiều dài và chiều rộng của SC, do SC sẽ là mỗi
miếng hình nhỏ).
Các giá trị Top, Left, Right, Bottom trong CauTruc có thể nhận các giá trị -1, 0, 1. Nếu Top nhận


giá trị 1 có nghĩa là hình chữ nhật sẽ kết hợp với hình ellipse (H.2), nếu nhận giá trị 0 nghĩa là
không có hình ellipse, còn giá trị -1 thì ellipse sẽ cắt hình chữ nhật (H.3) (tương tự cho Left,
Right và Bottom). Bạn sẽ thấy cách qui định giá trị này rất hữu ích trong các bước sau.
Sở dĩ khai báo Function CreateFormRegion là Private vì nếu bạn chuyển qua Public thì khi chạy
chương trình, VB sẽ báo lỗi là kiểu người dùng định nghĩa (CauTruc) không được làm đối số
(chỉ khi nào bạn tạo ActiveX Control riêng và biên dịch thành *.ocx mới không gặp lỗi này). Do
đó ta phải tạo 1 Sub có tính Public gọi Function này. Sub này sẽ trở thành
một Method của SC.
Public Sub DrawShape(vLeft As Long, vTop As Long, vRight As Long,
vBottom As Long)
Dim DrawStyle As CauTruc
Dim nRet As Long
DrawStyle.Left = vLeft
DrawStyle.Top = vTop
DrawStyle.Right = vRight
DrawStyle.Bottom = vBottom
nRet = SetWindowRgn(UserControl.hwnd, CreateFormRegion(1, 1, 0, 0, DrawStyle), True)
End Sub
Bây giờ thêm các Property phục vụ cho việc đồ họa:
Public Property get hWnd() as Long
hWnd=Usercontrol.hWnd
End Property
Public Property get hDC() as Long
hDC=Usercontrol.hDC
End Property
Để di chuyển SC (không có TitleBar) dễ
dàng, bạn phải "capture" chuột và mô phỏng việc nhấn
và rê chuột trái (khai báo thêm 2 API là ReleaseCapture và SendMessage ở phần khai báo các
hàm API):
Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As

Single)


ReleaseCapture
SendMessage UserControl.hwnd, &HA1, 2, 0&
End Sub
Ngoài ra, để có thêm nhiều sự kiện (Event) như MouseMove, MouseUp, Resize,... bạn khai báo
các sự kiện này ở đầu code:
Public <Tên sự kiện> (Các đối số)
Và khi UserControl xảy ra sự kiện nào thì bạn báo hiệu (RaiseEvent) sự kiện đó
Phần Form
Vấn đề kế tiếp là phải tạo các SC sao cho hợp lí vì các cạnh của SC (hay nói chính xác là các
hình ellipse) mang các giá trị ngẫu nhiên (nếu không, các SC sẽ có cùng hình dạng).Trước hết
tạo một hàm trả về giá trị ngẫu nhiên cho các cạnh:
Private Function NgauNhien() As Long
Randomize
If Rnd < 0.3333 Then
NgauNhien = -1
ElseIf Rnd > 0.3333 And Rnd < 0.6666 Then
NgauNhien = 0
Else
NgauNhien = 1
End If
End Function
Hàm Rnd sẽ trả về một con số trong khoảng giá trị [0-1] và Randomize sẽ sinh một con số mới
phục vụ cho việc lấy Rnd. Nếu không dùng Randomize thì mỗi lần form Load, Rnd sẽ trả về các
con số y hệt cũ.
Trở lại vấn đề tạo các cạnh sao cho hợp lí. Mời bạn xem hình 4. Ở đây tôi lần lượt đánh dấu các
hình theo thứ tự Trái-Đỉnh-Phải-Đáy. Theo đó, hai SC xếp trên cùng hàng sẽ có Phải trước+Trái
sau=0. Hai SC trên cùng cột sẽ có Đáy trên+Đỉnh dưới=0. Cách thuận tiện nhất là lưu các dữ liệu

này vào mảng 2 chiều cấu trúc (mỗi phần tử mảng là 1 cấu trúc theo kiểu CauTruc), sau đó tạo
các SC theo các phần tử này. Chúng ta khai báo lại kiểu CauTruc
Private Type CauTruc
Top as Long
Bottom as Long
Left as Long
Right as Long
End Type
Dim a(100,100) As CauTruc 'mảng có tối đa 101*101 phần tử
Sau cùng là chia hình ra thành nhiều mảnh nhỏ (thực chất mỗi mảnh nhỏ là một SC) và dùng
hàm BitBlt để copy vùng ảnh bên ảnh đích (ảnh cần cắt) qua SC.
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long,
ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long,
ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Const SRCCOPY = &HCC0020
- hDestDC là hDC của thiết bị nhận khối hình, X là hoành độ trên trái điểm bắt đầu nhận khối
hình, Y là tung độ trên trái điểm bắt đầu nhận khối hình, nWidth là chiều rộng khối hình,
nHeight là chiều cao khối hình (tất cả các đối số này là của
SC).
- hSrcDC là hDC của hình gốc, xSrc là hoành độ trên trái của
khối hình truyền đi, ySrc là tung độ trên trái của khối hình
truyền đi (của hình đích, cụ thể là một PictureBox).
- dwRop là cờ xác định hoạt động quét, ở đây là chỉ cần ki
ểu
quét y nguyên như cũ (SRCCOPY).
Sub sau sẽ sinh ra các mảnh hình bất kì:
Private Sub SinhHinh(ByVal wCount As Integer, ByVal hCount As Integer)
wCount là số mảnh muốn cắt theo chiều ngang, hCount là số mảnh muốn cắt theo chiều dọc.
picPicture là một PictureBox chứa hình đích. Nếu bạn nghĩ muốn chia tấm hình ra thành
wCount*hCount mảnh, mỗi mảnh(hay SC control) có chiều dài = chiều dài tấm hình/wCount và

chiều rộng = chiều rộng tấm hình/hCount, thì kết quả bạn đạt được sẽ không như ý muốn. Phần
chúng ta nhìn thấy được sau khi SC đã tạo hình chỉ là hình chữ nhật trong cùng (= 5/9 chiều dài
của SC) và các hình ellipse (nếu có). Do đó mỗi SC sẽ có chiều dài (hoặc rộng) thực =
(9/5*chiều dài tấm hình)/wCount (hoặc /hCount). Vì ta không biết người sử dụng sẽ cắt ra bao
nhiêu mảnh nên đầu tiên phải đưa lên form một SC và đặt index=0. Sau đó sẽ nạp các SC còn lại
tương ứng với số mảnh. Hàm ChieuDai(sLen) sẽ trả về 0 nếu sLen<=0 (cụ thể ở đây là =-1,0),
trường hợp khác trả về 1.
Private Function ChieuDai(ByVal sLen) As Integer
If sLen <= 0 Then
ChieuDai = 0
Else
ChieuDai = 1
End If
End Function
Sở dĩ có hàm ChieuDai này là vì khi copy mảnh hình từ picPicture ta không biết đưa khối hình
vào SC ở tọa độ X nào cho hợp lí (vì hình ellipse trái khi có khi không do tạo ngẫu nhiên), do đó
tọa độ X ở đây sẽ là tọa độ X của hình chữ nhật trong cùng (2/9*uW) trừ chiều dài hình ellipse
trái (nếu có, cũng =2/9*uW), tương tự cho tọa độ Y của SC, tọa độ X,Y của picPicture. Còn
chiều dài và cao phụ thuộc vào ellipse phải, trái, đỉnh, đáy, nên ngoài kích thước của hình chữ
nhật trong cùng (5/9*uW) phải cộng thêm chiều dài các ellipse (nếu có).
Bây giờ trò chơi cơ bản đã hoàn tất. Việc còn lại là thiết kế giao diện tùy vào thẩm mỹ của mỗi
người. Điều căn bản của trò chơi này là bạn phải có một khu vực chính dành cho các SC để tạo
lại hình, một PictureBox của hình nguồn làm nhiệm vụ hướng dẫn người chơi (hình này nhỏ
hơn
hình gốc) và một vùng chứa các hình đã cắt.
THỦ TỤC SINH RA CÁC MẢNH HÌNH BẤT KÌ:

×