Page({
data: {
score: 0,
fps: 60,
isGameOver: false
},
onLoad: function() {
this.cannonAngle = 45;
this.bullets = [];
this.balloons = [];
this.canvasWidth = 0;
this.canvasHeight = 0;
this.lastTime = Date.now();
this.isGameRunning = true;
const systemInfo = wx.getSystemInfoSync();
this.pixelRatio = systemInfo.pixelRatio;
this.setupCanvas();
this.startGame();
},
gameLoop: function() {
if (!this.isGameRunning) {
this.gameLoopRunning = false;
return;
}
this.gameLoopRunning = true;
const currentTime = Date.now();
const deltaTime = currentTime - this.lastTime;
const interval = 1000 / this.data.fps;
if (deltaTime > interval) {
this.updateGame();
this.drawGame();
this.lastTime = currentTime - (deltaTime % interval);
}
setTimeout(() => {
this.gameLoop();
}, interval);
},
setupCanvas: async function() {
const query = wx.createSelectorQuery();
query.select('#gameCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
this.canvas = canvas;
this.ctx = ctx;
this.canvasWidth = res[0].width;
this.canvasHeight = res[0].height;
this.cannonX = this.canvasWidth / 2;
this.cannonY = this.canvasHeight - 30;
this.lastTime = Date.now();
this.gameLoop();
this.generateBalloons();
});
},
startGame: function() {
this.setData({
score: 0,
isGameOver: false
});
this.isGameRunning = true;
this.bullets = [];
this.balloons = [];
this.lastTime = Date.now();
this.generateBalloons();
},
onTouchStart: function(e) {
if (!this.isGameRunning) return;
const touch = e.touches[0];
const pos = this.getCanvasPosition(touch);
this.updateCannonAngle(pos);
this.shoot();
},
onTouchMove: function(e) {
if (!this.isGameRunning) return;
const touch = e.touches[0];
const pos = this.getCanvasPosition(touch);
this.updateCannonAngle(pos);
},
getCanvasPosition: function(touch) {
return {
x: touch.pageX,
y: touch.pageY
};
},
updateCannonAngle: function(pos) {
const dx = pos.x - this.cannonX;
const dy = this.cannonY - pos.y;
let angle = Math.atan2(dy, dx);
angle = (angle * 180 / Math.PI);
angle = Math.max(0, Math.min(180, angle));
this.cannonAngle = angle;
},
shoot: function() {
if (!this.isGameRunning) return;
const angleRad = this.cannonAngle * Math.PI / 180;
const bulletSpeed = 15;
const bullet = {
x: this.cannonX,
y: this.cannonY,
vx: Math.cos(angleRad) * bulletSpeed,
vy: -Math.sin(angleRad) * bulletSpeed
};
this.bullets.push(bullet);
},
updateGame: function() {
if (!this.isGameRunning) return;
for (let i = this.bullets.length - 1; i >= 0; i--) {
const bullet = this.bullets[i];
bullet.x += bullet.vx;
bullet.y += bullet.vy;
if (bullet.y < 0 || bullet.y > this.canvasHeight ||
bullet.x < 0 || bullet.x > this.canvasWidth) {
this.bullets.splice(i, 1);
continue;
}
for (let j = this.balloons.length - 1; j >= 0; j--) {
const balloon = this.balloons[j];
const dx = bullet.x - balloon.x;
const dy = bullet.y - balloon.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
this.setData({
score: this.data.score + 10
});
this.balloons.splice(j, 1);
this.bullets.splice(i, 1);
break;
}
}
}
for (let i = this.balloons.length - 1; i >= 0; i--) {
const balloon = this.balloons[i];
balloon.y += balloon.speed;
if (balloon.y > this.canvasHeight) {
this.gameOver();
return;
}
}
},
drawGame: function() {
if (!this.ctx) return;
const ctx = this.ctx;
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
ctx.save();
ctx.translate(this.cannonX, this.cannonY);
ctx.rotate(-this.cannonAngle * Math.PI / 180);
ctx.fillStyle = '#333';
ctx.fillRect(0, -5, 40, 10);
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2);
ctx.fillStyle = '#444';
ctx.fill();
ctx.restore();
ctx.fillStyle = '#FF0000';
this.bullets.forEach(bullet => {
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, 5, 0, Math.PI * 2);
ctx.fill();
});
this.balloons.forEach(balloon => {
ctx.fillStyle = balloon.color || '#1E90FF';
ctx.beginPath();
ctx.arc(balloon.x, balloon.y, 15, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.moveTo(balloon.x, balloon.y + 15);
ctx.lineTo(balloon.x - 5, balloon.y + 25);
ctx.lineTo(balloon.x + 5, balloon.y + 25);
ctx.closePath();
ctx.fill();
});
},
generateBalloons: function() {
if (this.balloonTimer) {
clearInterval(this.balloonTimer);
}
this.balloonTimer = setInterval(() => {
if (!this.isGameRunning) return;
if (this.balloons.length < 5) {
this.balloons.push({
x: Math.random() * (this.canvasWidth - 40) + 20,
y: -20,
speed: Math.random() * 2 + 1,
color: '#1E90FF'
});
}
}, 2000);
},
gameOver: function() {
this.isGameRunning = false;
this.gameLoopRunning = false;
this.setData({ isGameOver: true });
if (this.balloonTimer) {
clearInterval(this.balloonTimer);
}
},
restartGame: function() {
this.bullets = [];
this.balloons = [];
this.cannonAngle = 45;
this.isGameRunning = true;
this.setData({
score: 0,
isGameOver: false
});
this.lastTime = Date.now();
if (this.balloonTimer) {
clearInterval(this.balloonTimer);
}
this.generateBalloons();
if (!this.gameLoopRunning) {
this.gameLoop();
}
},
onUnload: function() {
this.isGameRunning = false;
this.gameLoopRunning = false;
if (this.balloonTimer) {
clearInterval(this.balloonTimer);
}
}
});