AC_GAME_OBJECT
let AC_GAME_OBJECTS = [];
class AcGameObject {
constructor() {
AC_GAME_OBJECTS.push(this);
this.has_called_start = false; //有没有执行start函数
this.timedelta = 0; //当前帧距离上一帧的时间间隔
}
start() { //只会在第一次执行一次
}
update() { //每帧都会执行
}
before_destroy() { // 销毁前执行一次
}
destroy() { //销毁时执行
this.before_destroy()
for (let i = 0; i < AC_GAME_OBJECTS.length; i++) {
if (AC_GAME_OBJECTS[i] === this) {
AC_GAME_OBJECTS.splice(i, 1);
break;
}
}
}
}
let last_timestamp;
//每一帧执行一次
const AC_GAME_ANIMATION = (timestamp) => {
for (let i = 0; i < AC_GAME_OBJECTS.length; i++) {
let obj = AC_GAME_OBJECTS[i];
if(!obj.has_called_start) {
obj.start();
obj.has_called_start = true;
}else {
obj.timedelta = timestamp - last_timestamp;
obj.update();
}
}
last_timestamp = timestamp;
requestAnimationFrame(AC_GAME_ANIMATION)
}
requestAnimationFrame(AC_GAME_ANIMATION);
创建地图
class GameMap extends AcGameObject {
constructor(playground) {
super();
this.playground = playground;
this.$canvas = $(`<canvas></canvas>`);
this.ctx = this.$canvas[0].getContext('2d');
this.ctx.canvas.width = this.playground.width;
this.ctx.canvas.height = this.playground.height;
this.playground.$playground.append(this.$canvas);
}
start() {
}
update() {
this.render();
}
render() {
this.ctx.fillStyle = "rgba(0, 0, 0, 0.2)";
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
}
//在AcGamePlayground类中创建地图
this.game_map = new GameMap(this);
创建角色
class Player extends AcGameObject {
constructor(playground, x, y, radius, color, speed, is_me) {
super();
this.playground = playground;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.speed = speed;
this.is_me = is_me;
this.eps = 0.1; //精度误差,小于0.1就认为没有了
}
start() {
}
update() {
this.render();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
//在AcGamePlayground类中创建角色
this.players = [];
this.players.push(
new Player(this, this.width / 2, this.height / 2,
this.height * 0.05,"white",
this.height * 0.15, true)
);
控制角色移动
//Player构造函数
this.angle = 0; //角度
this.move_length = 0; //距离
//start函数
start() {
if(this.is_me) {
this.add_listening_events(); //鼠标监听事件
}
}
//鼠标监听事件
add_listening_events() {
let outer = this;
//阻止右键默认事件
this.playground.game_map.$canvas.on('contextmenu', () => {
return false;
});
//右键鼠标点击事件
this.playground.game_map.$canvas.mousedown((e) =>{
if(e.which === 3) {
outer.move_to(e.clientX, e.clientY);
}
})
}
//move_to
move_to(tx, ty) {
//当前点距离目标点的距离和角度
this.move_length = this.get_dist(this.x, this.y, tx, ty);
this.angle = Math.atan2(ty - this.y, tx - this.x)
}
//get_dist 获取两点间距离
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
//update
update() {
if (this.move_length < this.eps) {
this.move_length = 0; //恢复初始值
this.angle = 0;
}else {
//每一帧角色移动的距离 this.timedelta /1000 表示1帧为几秒
let distance = this.speed * this.timedelta / 1000;
//真正要移动的距离,防止移出界
let moved = Math.min(this.move_length, distance );
//横坐标x = 这一帧移动的距离 * 水平方向的分量
this.x += moved * Math.cos(this.angle);
this.y += moved * Math.sin(this.angle);
//减少移动距离
this.move_length -= moved;
}
this.render();
}
火球类
class FireBall extends AcGameObject {
constructor(playground, player, x, y, radius, angle, color, speed, move_length) {
super();
this.playground = playground;
this.player = player;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.angle = angle;
this.radius = radius;
this.color = color;
this.speed = speed;
this.move_length = move_length;
this.eps = 0.1;
}
start() {
}
update() {
if (this.move_length < this.eps) {
this.destroy();
return false;
}
let distance = this.speed * this.timedelta / 1000;
let moved = Math.min(this.move_length, distance );
this.x += moved * Math.cos(this.angle);
this.y += moved * Math.sin(this.angle);
this.move_length -= moved;
this.render();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
发射火球
//player的构造函数
this.cur_skill = null;
//add_listening_event()
//添加键盘监听事件
//键盘事件
$(window).keydown(function(e) {
if (e.which === 81) { //如果是'q'键
outer.cur_skill = "fireball";
}
});
//鼠标事件
if(e.which === 3) {
outer.move_to(e.clientX, e.clientY);
}else if (e.which === 1) { //左键
if (outer.cur_skill === 'fireball') {
outer.shoot_fireball(e.clientX, e.clientY);
outer.cur_skill = null;
}
}
shoot_fireball(tx, ty) {
let x = this.x, y = this.y;
let radius = this.playground.height * 0.01;
let angle = Math.atan2(ty - y, tx - x);
let color = "orange";
let speed = this.playground.height * 0.5;
let move_length = this.playground.height * 1.5;
new FireBall(this.playground, this, x, y, radius, angle, color, speed, move_length);
}
创建电脑玩家
//playground类,创建5个玩家
for (let i = 0; i < 5; i++) {
this.players.push(
new Player(this, this.width / 2, this.height / 2,
this.height * 0.05,"blue",
this.height * 0.15, false)
);
//随机移动
//player类
start() {
if(this.is_me) {
this.add_listening_events();
}else { //人机随机移动
let tx = Math.random() * this.playground.width;
let ty = Math.random() * this.playground.height;
this.move_to(tx, ty);
}
}
//update
if (this.move_length < this.eps) {
this.move_length = 0;
this.angle = 0;
//当停下时,在从新随机一个数
if (!this.is_me) {
let tx = Math.random() * this.playground.width;
let ty = Math.random() * this.playground.height;
this.move_to(tx, ty);
}
}
碰撞检测
//FireBall类
//constructor添加火球伤害
this.damage = damage; //20%
//update
for (let i = 0; i < this.playground.players.length; i++) {
let player = this.playground.players[i];
//发生碰撞
if (this.player !== player && this.is_collision(player)) {
this.attack(player); //攻击玩家
}
}
//判断碰撞
is_collision(player) {
let distance = this.get_dist(this.x, this.y, player.x, player.y);
return distance < this.radius + player.radius;
}
//产生撞击
attack(player) {
//撞击角度
let damage_angle = Math.atan2(player.y - this.y, player.x - this.x);
//玩家被撞击方法
player.is_attacked(damage_angle, this.damage)
this.destroy();
}
//player类
构造函数
this.damage_angle = 0;
this.damage_speed = 0;
this.friction = 0.9; //摩擦力
//被撞击
is_attacked(damage_angle,damage) {
this.radius -= damage ;
if (this.radius < 10) {
this.destroy();
return false;
}
this.damage_speed = damage * 100;
this.damage_angle = damage_angle;
}
//update
if (this.damage_speed > 10){
this.move_length = 0;
this.angle = 0;
this.x += Math.cos(this.damage_angle) * this.damage_speed * this.timedelta / 1000;
this.y += Math.sin(this.damage_angle) * this.damage_speed * this.timedelta / 1000;
this.damage_speed *= this.friction;
}
碰撞后的粒子效果
//particle类
class Particle extends AcGameObject{
constructor(playground, x, y, radius,angle, color, speed, move_length) {
super();
this.playground = playground;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.radius = radius;
this.angle = angle;
this.speed = speed;
this.friction = 0.9;
this.move_length = move_length;
this.eps = 3;
}
start() {
}
update() {
if (this.move_length < this.eps || this.speed < this.eps) {
this.destroy();
return false;
}
let distance = this.speed * this.timedelta / 1000;
let moved = Math.min(this.move_length, distance)
this.x += Math.cos(this.angle) * moved;
this.y += Math.sin(this.angle) * moved;
this.speed *= this.friction;
this.move_length -= moved;
this.render();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
//player
//is_attacked()
for (let i = 0; i < 10 + Math.random() * 5; i++) {
let x = this.x, y = this.y;
let radius = this.radius * Math.random() * 0.1;
let angle = Math.PI * 2 * Math.random();
let color = this.color;
let speed = this.speed * 10;
let move_length = this.radius * Math.random() * 5;
new Particle(this.playground, x, y, radius, angle, color, speed, move_length);
}
给玩家随机颜色
get_random_color() {
let colors = ['blue', 'green', 'red', 'yellow','pink'];
return colors[Math.floor(Math.random() * colors.length)];
}
人机随机发射火球
this.spend_time += this.timedelta / 1000;
if (!this.is_me && this.spend_time > 4 && Math.random() < 1 / 180.0) {
let player = this.playground.players[Math.floor(Math.random() * this.playground.players.length)];
let tx = player.x + Math.cos(this.angle) * player.speed * this.timedelta / 1000 * 0.3;
let ty = player.y + Math.sin(this.angle) * player.speed * this.timedelta / 1000 * 0.3;
this.shoot_fireball(tx, ty);
}
销毁玩家
before_destroy() {
for (let i = 0; i < this.playground.players.length; i++) {
if(this.playground.players[i] === this){
this.playground.players.splice(i, 1);
}
}
}