Bài này giải thích cách làm game
trong canvas, thường bạn đọc sách làm theo một game nào đó xong thì tự suy ra, ở
đây tôi viết mọi thứ cho rõ ràng, đơn giản.
Có rất nhiều cách làm game, như
dùng các game engine, công cụ chuyên nghiệp để làm. Nhưng mới học ta không cần
chúng, chỉ dùng Canvas cũng có thể làm được nhiều thứ.
Tạo một project bất kỳ, ta sẽ
làm ở class Main. Tạo thư mục drawable, copy vài cái ảnh icon nhỏ vào đó.
Copy vào class Main một class nữa,
cho vào trên ngoặc đóng cuối cùng.
public class gameview extends View {
public gameview(Context context) {
super(context);
}
@Override
protected void onDraw(final Canvas canvas)
{
// TODO
Auto-generated method stub
super.onDraw(canvas);
}
}
Class gameview này có hàm onDraw
là nơi vẽ nhân vật, điều khiển game.
Bây giờ ta vẽ nhân vật đầu tiên.
Copy dòng sau vào dưới dòng extends của class gameview.
private Bitmap mcuoi;
Copy dòng sau vào dưới super(context);
mcuoi =
BitmapFactory.decodeResource(getResources(), R.drawable.cuoi);
Copy dòng sau vào dưới super.onDraw(canvas);
canvas.drawBitmap(mcuoi, 50, 100, null);
Đưa gameview vào class Main bằng
cách khai báo đối tượng tên game lên trên dòng override của class Main.
gameview game;
Rào dòng
setcontentView lại và thêm 2 dòng sau
game=new gameview(this);
setContentView(game);
Chạy thử để thấy
nhân vật đã ra màn hình.
Nền ứng dụng có vẻ chưa trắng
lắm, thêm dòng sau game.setBackgroundColor(Color.WHITE);
vào trên dòng setContentView(game);
Ta vẽ nhân vật tại vị trí 50, 100, 50 là tọa độ x, 100 là
y.
Dù bạn để màn hình ngang hay dọc thì chiều ngang của thiết
bị luôn là x, dọc luôn là y. Nên lúc quay ngang thì x lớn hơn y.
Bây giờ để icon di chuyển, hãy khai báo biến int x=0; dưới dòng extends.
Sửa số 50 trong lệnh draw thành 50+x, thêm hai dòng
x=x+2;
invalidate();
Chạy thử để thấy icon đã di chuyển sang phải.
Tiếp tục thêm vào dưới dòng extends int y=0;
Sửa số 100 trong lệnh draw thành 100+y, thêm dòng
y=y+2;
Chạy thử để thấy icon di chuyển xuống dưới.
Bây giờ ta muốn icon di chuyển ngang qua màn hình, đi hết lại
chui ra đi tiếp thì thế nào?
Khai báo thêm hai biến r, c dưới dòng extends int r, c;
Thêm vào dưới dòng super(context);
DisplayMetrics metrics = getResources().getDisplayMetrics();
r = metrics.widthPixels;
c = metrics.heightPixels;
Sửa lệnh draw thành canvas.drawBitmap(mcuoi, x, 100, null);
Thêm lệnh if
if(x>r){
x=0;
}
Ta để là nếu khi nào x tăng lên lớn hơn độ rộng màn hình
thì nó quay về 0.
Để khi quay lại icon trông có vẻ chui ra từ bên trong lề
trái, ta thay vào trong lệnh if
x=-mcuoi.getWidth();
Ta lấy số âm của độ rộng icon, tức là mép phải của nó vừa
đúng bằng mép trái màn hình nên trông nó như đi vào từ bên trong.
Bây giờ ta muốn icon đi chạm lề phải thì đi ngược lại.
Khai báo một biến boolean phai=true;
Sửa code thành.
if(phai==true){
x=x+2;
}
else{
x=x-2;
}
if(x>r){
phai=false;
}
if(x<0){
phai=true;
}
Bây giờ ta muốn icon đi sang phải khi có chạm tay vào màn
hình, nếu không chạm nó tự lùi về bên trái.
Copy các dòng sau xuống dưới invalidate nhưng ở ngoài ngoặc.
@Override
public boolean onTouchEvent(MotionEvent event) {
switch
(event.getAction() & MotionEvent.ACTION_MASK) {
//nhấc tay
case MotionEvent.ACTION_UP:
phai = false;
break;
//chạm tay
case MotionEvent.ACTION_DOWN:
phai=true;
}
return true;
}
Chạy thử, lúc icon chạy về
trái hãy chạm tay vào, nó chạy sang phải, hết đường nó lại quay về dù vẫn đang
chạm tay, đó là vì khi đó lệnh if(x>r)có hiệu lực do nó lại được gọi sau hành vi chạm tay.
if(x>r){
phai=false;
}
Khi đang chạy chương trình, hàm invalidate() liên tục cập
nhật vị trí icon và các biến, do tọa độ x, y thay đổi nên icon đi chuyển, nếu
có biến nào phụ thuộc x,y nó cũng thay đổi theo.
Bây giờ copy một icon hình kim cương vào drawable.
Khai báo Bitmap kimcuong, khởi tạo.
kimcuong =
BitmapFactory.decodeResource(getResources(), R.drawable.kim);
Vẽ nó ra.
canvas.drawBitmap(kimcuong, 100, y, null);
Làm nó di chuyển xuống dọc màn hình
y=y+2;
Chạy thử để thấy nó đã đi xuống và mất tích luôn, kệ nó, để
đơn giản ta kệ nó thế đã.
Bây giờ ta muốn khi có va chạm giữa hai icon thì phát ra âm
thanh.
Thêm các dòng sau lên trên invalidate()
Paint paint = new Paint();
paint.setColor(Color.parseColor("#FF00FF"));
Rect touch1 = new Rect(x, 100,x+mcuoi.getWidth(),
100+mcuoi.getHeight());
Rect touch2 = new Rect(100, y,100+mcuoi.getWidth(), y+mcuoi.getHeight());
canvas.drawRect(x, 100,x+mcuoi.getWidth(), 100+mcuoi.getHeight(),
paint);
canvas.drawRect(100,
y,100+mcuoi.getWidth(), y+mcuoi.getHeight(),
paint);
Ta vẽ các Rect hình chữ nhật bao lấy icon, kiểm tra vẽ đúng
chưa bằng cách vẽ các hình tô màu đè lên chúng, chạy thử nếu thấy đúng vị trí
khi icon di chuyển là được rồi.
Sau đó comment 2 dòng canvas.drawRectđi để ta còn nhìn thấy icon.
Nếu icon của bạn có các khoảng trắng bao quanh, hình chữ nhật
trông sẽ to hơn vì nó bao toàn bộ cái hình đó. Nếu muốn nó bé lại, ta chỉnh x+mcuoi.getWidth()thành x+20 chẳng hạn, tùy bạn ước lượng cho
vừa khít thì khi va chạm nó mới chuẩn.
Tránh trường hợp lệch quá, giả sử
ta làm game bắn máy bay đạn chưa chạm tới thì máy bay đã nổ tung rồi nó vô lý.
Copy thêm các dòng sau vào.
if (Rect.intersects(touch1, touch2)) {
try {
Uri notification = RingtoneManager
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone
r = RingtoneManager.getRingtone(
getApplicationContext(), notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
}
Ta dùng các Rect đã vẽ để lấy va chạm, tạo tiếng chuông
báo.
Chạy thử, chạm tay điều chỉnh để icon chạm nhau, thay đổi tọa
độ nếu khó chạm quá, phải có tiếng chuông khi va chạm mới đúng.
Bây giờ ta muốn nếu có va chạm thì icon mặt cười bắn vào giữa
màn hình đứng im, và hiện chữ Game over to tướng ra màn hình.
Hãy tạo một biến boolean over=false;
Bạn tự biết phải để nó chỗ nào.
Trong lệnh set intersects, thêm dòng over=true;
Copy thêm các dòng sau lên trên invalidate()
if(over==true){
x=r/2;
paint.setTextSize(60);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Game Over", r / 2, 100, paint);
}
Chạy thử để thấy kết quả.
Bây giờ ta lại muốn sau khi game over, nếu người dùng chạm
tay vào màn hình thì chơi lại được từ đầu.
Copy đoạn sau vào trong case MotionEvent.ACTION_DOWN:
if(over==true){
over=false;
x=-mcuoi.getWidth();
y=-kimcuong.getHeight();
}
Ta dùng lệnh if, nếu over chuyển sang true tức là lúc game
over ta set nó là là false, đồng thời cho tọa độ icon về khởi đầu, ta cho nó
chui kín vào trong bằng trừ độ rộng, cao, nếu để bằng 0 nó sẽ xuất hiện lại
ngoài mép.
Hiện lúc over icon kimcuong vẫn chạy xuống, muốn tất cả dừng
lại, ta cho các lệnh làm chúng chạy vào trong lệnh if(over==false){
Bây giờ thay vì chạm là over, ta muốn đó là ăn điểm, ghi ra
màn hình.
Khai báo một biến int an=0; lên trên.
Trong lệnh intersects, rào dòng over=true; thành comment, thêm
dòng này vào y=-100;
Copy các dòng sau lên trên invalidate()
if(y==-100){
an=an+1;
}
paint.setTextSize(33);
canvas.drawText("Score:
"+an, r/2-10, 25, paint);
Mỗi khi va chạm, ta bật y về
-100 để làm biến mất icon kimcuong, đồng thời trong lệnh if(y==-100) ta tăng biến an lên 1, ghi nó ra màn hình, lệnh invalidate() đảm bảo số điểm
được cập nhật liên tục.
Chạy thử, chạm tay vào màn hình để icon va chạm nhau, mỗi lần
lại thêm một điểm ghi ra Score.
Bài này ta đã thấy các yếu tố cơ bản để làm game trong
canvas, để xem những tùy biến phức tạp hơn, hãy xem bài làm game Flappy bird.
No comments:
Post a Comment