12/08/2018, 13:16

Làm game 2D với Javascript thuần (phần 3)

Bài viết này được dịch dựa trên nguồn sau đây. DEMO : https://jsfiddle.net/erajpypL/ Tạm thời cho đến thời điểm hiện tại thì bạn cũng xem như là done được 1 game đập bóng đơn giản rồi đấy. Các chương tiếp theo chỉ là thêm thắt bổ sung chức năng cho nó trong khó lên tí thôi. Đợt này mình ...

Bài viết này được dịch dựa trên nguồn sau đây.

Screenshot from 2016-03-23 10:06:25.png

DEMO : https://jsfiddle.net/erajpypL/

Tạm thời cho đến thời điểm hiện tại thì bạn cũng xem như là done được 1 game đập bóng đơn giản rồi đấy. Các chương tiếp theo chỉ là thêm thắt bổ sung chức năng cho nó trong khó lên tí thôi.

Đợt này mình sẽ làm mấy cái viên gạch ở trên. Ban nảy bóng vào gạch rồi phá vỡ các viên gạch phía trên đó.

THIÊT LẬP BIẾN "VIÊN GẠCH"

Đầu tiên thì mình định nghĩa số hàng và cột của đống gạch, rồi thì là độ rộng độ cao padding của các viên gạch đó. sao cho nó ko gần nhau quá.

var brickRowCount = 3;
var brickColumnCount = 5;
var brickWidth = 75;
var brickHeight = 20;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = 30;

Mình sẽ render ra những viên gạch ở phía trên. Đầu tiên thì dùng 2 vòng for lồng nhau để có thể định nghĩa đc một mảng 2 chiều. Mỗi viên gạch sẽ chứa một object mà trong object này chứa vị trí x và y để vẽ viên gạch đó lên màn hình.

var bricks = [];
for(c=0; c<brickColumnCount; c++) {
    bricks[c] = [];
    for(r=0; r<brickRowCount; r++) {
        bricks[c][r] = { x: 0, y: 0 };
    }
}

VẼ GẠCH

Giống với những lần render khác.

function drawBricks() {
    for(c=0; c<brickColumnCount; c++) {
        for(r=0; r<brickRowCount; r++) {
            bricks[c][r].x = 0;
            bricks[c][r].y = 0;
            ctx.beginPath();
            ctx.rect(0, 0, brickWidth, brickHeight);
            ctx.fillStyle = "#0095DD";
            ctx.fill();
            ctx.closePath();
        }
    }
}

THIẾT LẬP VA CHẠM

TIếp là mình sẽ thiết lập va chạm giữa bóng và gạch. Trước hết mình sẽ duyệt hết mảng 2 chiều "gạch". Làm 1 biến b để lưu chính xác nó là phần tử nào trong mảng.

function collisionDetection() {
    for(c=0; c<brickColumnCount; c++) {
        for(r=0; r<brickRowCount; r++) {
            var b = bricks[c][r];
            // calculations
        }
    }
}

Nếu tâm của quá bóng nằm trong tọa độ của 1 trong số những viên gạch, chúng ta sẽ chuyển hướng di chuyển của bóng. Chúng ta sẽ cân nhắc 4 cases :

  1. Tọa độ x của bóng lơn hơn tọa độ y của gạch
  2. Tọa độ x của bóng bé hơn tao độ x của gạch + chiều rộng của gạch
  3. Tọa đọ y của bóng lơn hơn tạo độ y của gạch
  4. Tọa độ y của bóng bé hơn tạo độ y của gạch + chiều cao của gạch

Code sẽ như sau :

function collisionDetection() {
    for(c=0; c<brickColumnCount; c++) {
        for(r=0; r<brickRowCount; r++) {
            var b = bricks[c][r];
            if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
                dy = -dy;
            }
        }
    }
}

CHO GẠCH BIẾN MẤT KHI BÓNG CHẠM VÀO

Đoạn code bên trên sẽ chạy ngon phần va chạm rồi. Bây giờ tiếp đến là mình phải làm làm sao để gạch biến mất cho bóng chạm vào. Mình sẽ làm việc này bằng cách cho thêm 1 param nữa để xác định liệu có cần phải render viên gạch trên từng screen hay ko ? Mình sẽ xử lý đoạn này ở lúc mình initialize đóng gạch.

Đầu tiên add thêm 1 thuộc tính status cho từng object gạch

var bricks = [];
for(c=0; c<brickColumnCount; c++) {
    bricks[c] = [];
    for(r=0; r<brickRowCount; r++) {
        bricks[c][r] = { x: 0, y: 0, status: 1 };
    }
}

Sau đó trong hàm drawBricks() trước khi mình vẽ gạch mình sẽ check status nếu là 1 thì vẽ nó còn nếu là 0 thì ko vẽ gì cả. Vì trước đó nó bị bóng chạm vào rồi.

function drawBricks() {
    for(c=0; c<brickColumnCount; c++) {
        for(r=0; r<brickRowCount; r++) {
            if(bricks[c][r].status == 1) {
                var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
                var brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
                bricks[c][r].x = brickX;
                bricks[c][r].y = brickY;
                ctx.beginPath();
                ctx.rect(brickX, brickY, brickWidth, brickHeight);
                ctx.fillStyle = "#0095DD";
                ctx.fill();
                ctx.closePath();
            }
        }
    }
}

UPDATE BIẾN TRẠNG THÁI CỦA GẠCH

Phần còn lại mình cần làm là cập nhật thuộc tính status khi mà có sự kiện va chạm giữa bóng và gạch xảy ra.

function collisionDetection() {
    for(c=0; c<brickColumnCount; c++) {
        for(r=0; r<brickRowCount; r++) {
            var b = bricks[c][r];
            if(b.status == 1) {
                if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
                    dy = -dy;
                    b.status = 0;
                }
            }
        }
    }
}

Mời các bạn theo dõi tiếp phần 4

0