Từ ví dụ trước về Di chuyển đối tượng bằng bàn phím, tôi tiếp tục thêm phát triển thêm một số tính năng để làm một đối tượng xe tăng có thể di chuyển bằng bàn phím và xoay góc, bắn đạn bằng chuột.
Xoay góc súng theo chuộtTrong bài viết trước, bạn đã biết cách để tính và xoay góc đối tượng trong canvas dựa vào hai giá trị speedY và speedX. Hai giá trị này chính là cạnh đối và cạnh kề của một tam giác vuông. Phát triển thêm một chút, tôi muốn góc của súng (cannon) sẽ tự động xoay theo góc di chuyển của chuột. Cách thực hiện điều này được thể hiện trong phương thức sau, với targetX và targetY là vị trí của chuột trên canvas. // tank.js: Tank.prototype.rotateCannon = function(targetX, targetY){ var dx = targetX - this.cx; var dy = targetY - this.cy; this.angle = Math.atan2(dy,dx); }c // sample.html: function canvas_mousemove(e){ var x = e.pageX - _canvas.offsetLeft; var y = e.pageY - _canvas.offsetTop; _tank.rotateCannon(x,y); } Ball – ĐạnMỗi viên đạn được bắn ra là một instance của lớp Ball. Phạm vi di chuyển của viên đạn sẽ được giới hạn trong “life-space”, ra ngoài phạm vi này, ta sẽ xóa nó ra khỏi danh sách lưu trữ dựa vào kết quả trả về của phương thức update(). Các tham số của constructor có nhiệm vụ:
function Ball(mapWidth, mapHeight,startX,startY,directionX,directionY){ this.power = 5; this.radius = 4; // the "life-space" this.minX = this.radius; this.minY = this.radius; this.maxX = mapWidth - this.radius; this.maxY = mapHeight - this.radius; this.speedX = directionX * this.power; this.speedY = directionY * this.power; this.cx = startX; this.cy = startY; } Ball.prototype.draw = function(context){ context.fillStyle = "black"; context.beginPath(); context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true); context.closePath(); context.fill(); } Ball.prototype.update = function(){ this.cx += this.speedX; this.cy += this.speedY; if(this.cx < this.minX || this.cx > this.maxX || this.cy < this.minY || this.cy > this.maxY) return true; return false; } Tank: bắn đạnLớp này sẽ chứa một mảng lưu trữ các viên đạn được bắn ra với tên balls. Khi được lệnh “Bắn!”, phương thức fire() sẽ được gọi để tạo một instance mới của Ball và thêm vào mảng balls. Tank.prototype.fire = function(){ var directionX = Math.cos(this.angle); var directionY = Math.sin(this.angle); var startX = this.cx + this.cannonWidth * directionX; var startY = this.cy + this.cannonWidth * directionY; var ball = new Ball(this.mapWidth,this.mapHeight,startX,startY,directionX,directionY); this.balls.push(ball); } Với hướng bắn được lưu trong biến this.angle, và độ lớn của lực bắn được xác định là 1, ta tạo được một tam giác vuông có: - Cạnh huyền = 1 đơn vị. - Góc alpha = this.angle. Như vậy ta sẽ tính được hai giá trị directionX và directionY bằng phép toán cos và sin đơn giản, hai giá trị này tạo thành một vector đơn vị do có độ lớn bằng 1. Từ vector đơn vị này, ta có thể dễ dàng xác định được vị trí xuất hiện của viên đạn dựa vào vị trí hiện tại của tank. Bạn cũng có thể giới hạn số lượng đạn bắn ra một lúc của tank bằng cách kiểm tra giá trị của this.balls.length. Vòng lặp gameMỗi lớp tôi tạo ra đều chứa hai phương thức là update() và draw(). Hai phương thức này của mỗi instance có tác dụng cập nhật và vẽ lại chính nó trên canvas. Tôi chỉ cần gọi một hàm update() duy nhất trên trang html và nó sẽ tự động gọi update() xuống các đối tượng bên trong (tank – > balls): // sample.hml: window.setInterval(update,50); // ... function update() { _tank.update(); draw(); } // tank.js: Tank.prototype.update = function(){ for(var i=0;i<this.balls.length;i++) { var item = this.balls[i]; item.update(); if(item.update()) { this.balls.splice(i,1); } } } // ball.js: Ball.prototype.update = function(){ this.cx += this.speedX; this.cy += this.speedY; if(this.cx < this.minX || this.cx > this.maxX || this.cy < this.minY || this.cy > this.maxY) return true; return false; } Mã nguồn hoàn chỉnh
ball.js: function Ball(mapWidth, mapHeight,startX,startY,directionX,directionY){ this.power = 5; this.radius = 4; // the "life-space" this.minX = this.radius; this.minY = this.radius; this.maxX = mapWidth - this.radius; this.maxY = mapHeight - this.radius; this.speedX = directionX * this.power; this.speedY = directionY * this.power; this.cx = startX; this.cy = startY; } Ball.prototype.draw = function(context){ context.fillStyle = "black"; context.beginPath(); context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true); context.closePath(); context.fill(); } Ball.prototype.update = function(){ this.cx += this.speedX; this.cy += this.speedY; if(this.cx < this.minX || this.cx > this.maxX || this.cy < this.minY || this.cy > this.maxY) return true; return false; } tank.js: function Tank(mapWidth, mapHeight){ this.mapWidth = mapWidth; this.mapHeight = mapHeight; this.radius = 20; this.speedX = 0; this.speedY = 0; this.power = 3; this.cx = mapWidth/2; this.cy = mapHeight/2; this.angle = 0; this.balls = []; this.cannonHeight = this.radius/2; this.cannonWidth = this.cannonHeight*3; } Tank.prototype.draw = function(context){ context.beginPath(); context.fillStyle = "green"; context.arc(this.cx,this.cy,this.radius,0,Math.PI*2,true); context.closePath(); context.fill(); // cannon context.save(); context.translate(this.cx,this.cy); context.rotate(this.angle); context.beginPath(); context.fillStyle = "red"; context.rect(0,-this.cannonHeight/2,this.cannonWidth,this.cannonHeight); context.closePath(); context.fill(); context.restore(); // yellow circle context.beginPath(); context.fillStyle = "yellow"; context.arc(this.cx,this.cy,this.radius/2,0,Math.PI*2,true); context.closePath(); context.fill(); for(var i=0;i<this.balls.length;i++) { this.balls[i].draw(context); } } Tank.prototype.update = function(){ for(var i=0;i<this.balls.length;i++) { var item = this.balls[i]; item.update(); if(item.update()) { this.balls.splice(i,1); } } } Tank.prototype.fire = function(){ if(this.balls.length > 4) return; var directionX = Math.cos(this.angle); var directionY = Math.sin(this.angle); var startX = this.cx + this.cannonWidth * directionX; var startY = this.cy + this.cannonWidth * directionY; var ball = new Ball(this.mapWidth,this.mapHeight,startX,startY,directionX,directionY); this.balls.push(ball); } Tank.prototype.rotateCannon = function(targetX, targetY){ var dx = targetX - this.cx; var dy = targetY - this.cy; this.angle = Math.atan2(dy,dx); } Tank.prototype.move = function(){ this.cx += this.speedX; this.cy += this.speedY; this.left = this.cx - this.radius; this.top = this.cy - this.radius; this.right = this.cx + this.radius; this.bottom = this.cy + this.radius; } Tank.prototype.resetSpeed = function(){ this.speedX = 0; this.speedY = 0; } Tank.prototype.moveUp = function(){ this.speedY = -this.power; } Tank.prototype.moveDown = function(){ this.speedY = this.power; } Tank.prototype.moveLeft = function(){ this.speedX = -this.power; } Tank.prototype.moveRight = function(){ this.speedX = this.power; } sample.html: <html> <head> <script src="tank.js"></script> <script src="ball.js"></script> <script src="keys.js"></script> <script type="text/javascript"> const AVAILABLE_KEYS = [ Keys.KEY_W, Keys.KEY_S, Keys.KEY_A, Keys.KEY_D ]; var _canvas; var _context; var _tank; var _keypressed = {}; function clear() { _context.clearRect(0, 0, _canvas.width, _canvas.height); } function init() { _canvas = document.getElementById("canvas"); _context = _canvas.getContext("2d"); _tank = new Tank(_canvas.width,_canvas.height); draw(); } function draw() { clear(); _tank.draw(_context); } function update() { _tank.update(); draw(); } function canvas_mousemove(e){ var x = e.pageX - _canvas.offsetLeft; var y = e.pageY - _canvas.offsetTop; _tank.rotateCannon(x,y); } function canvas_mousedown(e){ _tank.fire(); } function canvas_keyDown(e){ if(AVAILABLE_KEYS.indexOf(e.keyCode)!=-1) { _keypressed[e.keyCode] = true; doKeypress(); } } function canvas_keyUp(e){ if(_keypressed[e.keyCode]) { _keypressed[e.keyCode] = false; _tank.resetSpeed(); } } function doKeypress(){ if(_keypressed[Keys.KEY_W]) _tank.moveUp(); if(_keypressed[Keys.KEY_S]) _tank.moveDown(); if(_keypressed[Keys.KEY_A]) _tank.moveLeft(); if(_keypressed[Keys.KEY_D]) _tank.moveRight(); _tank.move(); draw(); } window.onload = function(){ init(); _canvas.onkeydown = canvas_keyDown; _canvas.onkeyup = canvas_keyUp; _canvas.onmousemove = canvas_mousemove; _canvas.onmousedown = canvas_mousedown; window.setInterval(update,50); } </script> </head> <body> <p>* Using W, A, S, D to move</p> <canvas id="canvas" width="400" height="400" tabindex="1" style="border: 1px solid;cursor: crosshair" ></canvas> </body> </html> Phần tiếp theoThêm các chướng ngại vật bay ngẫu nhiên để xe tăng có thể né tránh và phá hủy. |
HTML5 – Canvas: Game xe tăng đơn giản – part 1
Ý kiến bạn đọc
Tin tức khác
Html5 – Làm game Battle City
- 6/8/2012
Html5 – WebSocket và Node.js
- 24/7/2012
Html5-Canvas: Viết game Tower Defense – part 4(end)
- 18/7/2012
Html5-Canvas: Viết game Tower Defense – part 3
- 13/7/2012
Html5-Canvas: Viết game Tower Defense – part 2
- 12/7/2012
Html5-Canvas: Viết game Tower Defense – part 1
- 12/7/2012
Html5-Canvas: Viết game Mario – part 5 (end)
- 12/7/2012
HTML5-Canvas: Viết game Mario – Part 4
- 10/7/2012
HTML5-Canvas: Viết game Mario – Part 3
- 10/7/2012
HTML5-Canvas: Viết game Mario – Part 2
- 6/7/2012
Tin tiêu điểm
-
Html5 – Canvas: Viết game bắn đại bác – part1 (14,985)
-
Html5 – WebSocket và Node.js (11,538)
-
Html5-Canvas: Viết game Tower Defense – part 1 (7,155)
-
HTML5 – Canvas: Di chuyển đối tượng bằng bàn phím (6,928)
-
Html5-Canvas: Viết game Tower Defense – part 4(end) (6,847)
-
HTML5 – Canvas: Vẽ đồng hồ kim (analog clock) (6,739)
-
Html5 – Làm game Battle City (6,195)
-
Html5-Canvas: Viết game Rắn Săn Mồi (6,153)
-
Html5-Canvas: Viết game Mario – part 5 (end) (5,942)
-
HTML5-Canvas: Viết game Mario – Part 3 (5,879)
Gallery
Text Links
Thiết kế logo chuyên nghiệp Insky
DAFABET
W88 w88b.com/dang-ky-tai-khoan-w88
W88
ca do bong da online
DAFABET
W88 w88b.com/dang-ky-tai-khoan-w88
W88
ca do bong da online
Tags
asp.net
JavaScript
Lập trình
Cơ sở dữ liệu
jquery
Csharp
Ajax
Thủ thuật
JavaScript
menu
Sql Server
Lập trình C#
WebService
stty
Sql
Phân trang
Rewrite
Mã hoá
Backup
Thủ thuật lập trình
Store procedure
Accordion
Validation
Store
Upload
Slide
jQueryPlugin
StoreProcedure
Regular Expression
Regex
android
Quick and snow
HTML5
WPF
WCF
Copyright © 2011 - 2012 vietshare.vn
by
phamkhuong102@gmail.com doanhkisi2315@gmail.com. All rights reserved.