If you're seeing this message, it means we're having trouble loading external resources on our website.

Nếu bạn đang sử dụng bộ lọc web, vui lòng kiểm tra lại xem bộ lọc có chặn hai tên miền *.kastatic.org*.kasandbox.org hay không.

Nội dung chính

Các kiểu hạt

Trong bài này, chúng ta sẽ sử dụng các kỹ thuật lập trình hướng đối tượng nâng cao hơn như tính kế thừa để tiếp tục phát triển hệ thống hạt. Nếu bạn chưa hiểu rõ về kiến thức này, hãy xem lại bài Tính kế thừa của đối tượng trong Chương Mở đầu về JavaScript trước khi tiếp tục bài học dưới đây.
Bây giờ, chúng ta sẽ sử dụng tính kế thừa để tạo ra các kiểu đối tượng con từ kiểu đối tượng Particle. Các kiểu hạt này có nhiều điểm chung với kiểu đối tượng "mẹ" về mặt chức năng nhưng cũng có những điểm riêng biệt cho từng loại.
Hãy cùng xem lại một chương trình đơn giản có sử dụng kiểu đối tượng Particle:
var Particle = function(position) {
  this.acceleration = new PVector(0, 0.05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = position.get();
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

Particle.prototype.display = function() {
  fill(127, 127, 127);
  ellipse(this.position.x, this.position.y, 12, 12);
};
Sau đây, chúng ta sẽ tạo ra một kiểu đối tượng mới dựa trên Particle, có tên là Confetti. Chúng ta sẽ bắt đầu bằng một hàm khởi tạo nhận cùng một lượng đối số và truyền hàm khởi tạo Particle vào:
var Confetti = function(position) {
  Particle.call(this, position);
};
Để đảm bảo các đối tượng Confetti của chúng ta có chung phương thức như các đối tượng Particle, chúng ta cần chỉ rõ nguyên mẫu của chúng phải dựa trên nguyên mẫu của Particle.
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
Đến đây, ta đã có các đối tượng Confetti hoạt động y hệt các đối tượng Particle. Hãy lưu ý rằng tính thừa kế không phải để giúp chúng ta tạo ra các bản sao giống nhau hoàn toàn mà để tạo ra những đối tượng có thể có chung nhiều chức năng nhưng cũng khác biệt ở một số mặt. Vậy điểm khác biệt của đối tượng Confetti ở đây là gì? Nếu dựa vào tên gọi, ta có thể thấy đối tượng Particle là các hạt nên thường được biểu diễn bằng hình elip. Còn confetti lại có nghĩa là pháo giấy nên thường được biểu diễn bằng các mảnh giấy vuông nhỏ. Vì vậy, trong trường hợp này, ta nên thay đổi phương thức display để thể hiện đối tượng confetti dưới dạng hình chữ nhật:
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255, this.timeToLive);
  stroke(0, 0, 0, this.timeToLive);
  strokeWeight(2);
  rect(0, 0, 12, 12);
};
Cuối cùng, chúng ta có một chương trình tạo ra một đối tượng Particle và một đối tượng Confetti. Hãy chú ý rằng hai kiểu đối tượng này hoạt động tương tự nhau nhưng lại khác nhau về hình dáng.

Thêm khả năng quay

Hãy thử tăng độ khó cho bài thực hành này nhé! Giả sử chúng ta muốn hạt Confetti có khả năng tự quay quanh chính nó khi bay trong không gian. Vậy chúng ta có thể vận dụng mô hình về vận tốc và gia tốc góc như chúng ta đã làm trong bài Dao động. Tuy nhiên, chúng ta cũng có thể thử mẹo nhanh chóng dưới đây.
Một hạt sẽ có hoành độ x nằm trong khoảng từ 0 đến chiều rộng của cửa sổ máy tính. Giả sử khi hoành độ x của hạt là 0, ta muốn góc quay của hạt là 0; khi hoành độ x của hạt bằng với chiều rộng, thì ta muốn góc quay của hạt sẽ bằng TWO_PI (2pi). Như vậy, với mỗi giá trị hoành độ x nằm trong một khoảng nhất định, ta đang muốn ánh xạ sang một giá trị góc quay tương ứng nằm trong một khoảng giá trị khác. Do đó, chúng ta có thể sử dụng hàm map() của ProcessingJS để tính toán các giá trị góc quay tương ứng với mỗi hoành độ x của hạt.
var theta = map(this.position.x, 0, width, 0, TWO_PI);
Và để tăng tốc độ quay, ta có thể ánh xạ phạm vi góc từ 0 lên TWO_PI*2. Sau đó, hãy đưa đoạn mã này vào trong phương thức display().
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255);
  stroke(0, 0, 0);
  strokeWeight(2);
  pushMatrix();
  translate(this.position.x, this.position.y);
  var theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
  rotate(theta);
  rect(0, 0, 12, 12);
  popMatrix();
};
Ta có kết quả như dưới đây, bạn hãy thử nhấn nút khởi động lại để xem hiệu ứng quay:
Chúng ta cũng có thể dựa trên tung độ y để tính góc theta, nhưng cách này sẽ tạo ra hiệu ứng hơi khác một chút. Tại sao lại như vậy? Như chúng ta biết, hạt có một gia tốc khác 0 không đổi theo hướng y (phương thẳng đứng). Điều này có nghĩa là vận tốc theo hướng y là một hàm bậc nhất theo thời gian và giá trị tung độ y là một hàm bậc hai theo thời gian. Ta có thể thấy điều đó qua các biểu đồ giá trị tung độ, vận tốc và gia tốc sau đây (những biểu đồ này được tạo ra dựa trên chương trình trên):
Như vậy, nếu chúng ta quay đối tượng confetti dựa trên giá trị tung độ y thì đối tượng sẽ quay theo hình parabol. Mặc dù chuyển động của pháo giấy trong thực tế phức tạp hơn quy tắc chúng ta đang thiết lập ở đây, nhưng bạn hãy thử chạy chương trình và xem chuyển động của vật thể chân thật tới đâu nhé! Ngoài ra, bạn cũng có thể nghĩ thêm các hàm khác để biểu diễn chuyển động của pháo giấy sát với thực tế hơn nữa.

Xây dựng hệ thống gồm nhiều kiểu hạt

Sau khi tạo được các nguyên mẫu kiểu đối tượng, chúng ta sẽ tiếp tục tạo ra nhiều đối tượng ParticleConfetti. Ở bài trước, ta đã sử dụng đối tượng ParticleSystem để quản lý cả hệ thống hạt Particle, vậy ta có thể mở rộng đối tượng này, sao chép những câu lệnh đã có và áp dụng tương tự để tạo các đối tượng Confetti:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = [];
};

ParticleSystem.prototype.addParticle = function() {
    this.particles.push(new Particle(this.origin));
    this.confettis.push(new Confetti(this.origin));
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
  }
for (var i = this.confettis.length-1; i >= 0; i--) {
    var p = this.confettis[i]; p.run();
  }
};
Trong cách làm trên, ta cần lưu ý rằng chúng ta đang có hai mảng riêng biệt, một cho các particle và một cho confetti. Mỗi khi thay đổi điều gì đó trong mảng particle, ta cũng phải thực hiện tương tự với mảng confetti. Điều này gây ra sự trùng lặp và phiền phức vì chúng ta phải viết gấp đôi số lượng mã cần thiết. Để tránh sự trùng lặp này, ta có thể chỉ sử một mảng duy nhất thay vì hai mảng. Điều này khả thi vì JavaScript cho phép lưu trữ nhiều kiểu đối tượng khác nhau trong một mảng. Bên cạnh đó, cả hai kiểu đối tượng ở đây đều có chung giao diện phương thức run(). Vì vậy, ta sẽ dùng một mảng duy nhất để lưu trữ cả hai kiểu đối tượng và cho vòng lặp chạy qua mảng này. Đồng thời, ta sẽ đơn giản hóa bước thêm đối tượng vào mảng, bằng cách thay đổi phương thức addParticle để quyết định ngẫu nhiên đối tượng nào sẽ được thêm vào.
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  var r = random(1);
  if (r < 0.5) {
    this.particles.push(new Particle(this.origin));
  } else {
    this.particles.push(new Confetti(this.origin));
  }
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
    if (p.isDead()) {
      this.particles.splice(i, 1);
    }
  }
};
Dưới đây là toàn bộ chương trình:

Tham gia cuộc thảo luận?

Chưa có bài đăng nào.
Bạn có hiểu Tiếng Anh không? Bấm vào đây để thấy thêm các thảo luận trên trang Khan Academy Tiếng Anh.