Django框架 (七)实现联机对战
1.统一长度单位
acapp/game/static/js/src/playground/zbase.js
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`<div class="ac-game-playground"></div>`);
this.hide();
this.root.$ac_game.append(this.$playground);
this.start();
}
get_random_color() {
let colors = ["blue", "red", "pink", "grey", "green"];
return colors[Math.floor(Math.random() * 5)];
}
start() {
let outer = this;
$(window).resize(function() {
outer.resize();
});
}
resize() {
this.width = this.$playground.width();
this.height = this.$playground.height();
let unit = Math.min(this.width / 16, this.height / 9);
this.width = unit * 16;
this.height = unit * 9;
this.scale = this.height;
if (this.game_map) this.game_map.resize();
}
show() {
this.$playground.show();
this.resize();
this.width = this.$playground.width();
this.height = this.$playground.height();
this.game_map = new GameMap(this);
this.players = [];
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, "white", this.height * 0.15, true));
for (let i = 0; i < 5; i ++ ) {
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, this.get_random_color(), this.height * 0.15, false));
}
}
hide() {
this.$playground.hide();
}
}
acapp/game/static/js/src/playground/game_map/zbase.js
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() {
}
resize() {
this.ctx.canvas.width = this.playground.width;
this.ctx.canvas.height = this.playground.height;
this.ctx.fillStyle = "rgba(0, 0, 0, 1)";
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
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);
}
}
改变css使背景为灰色,使canvas居中 acapp/game/static/css/game.css
.ac-game-playground{
width: 100%;
height: 100%;
user-select:none;
background-color:grey;
}
.ac-game-playground > canvas{
position:relative;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
2.元素相对位置改变
acapp/game/static/js/src/playground/zbase.js 在初始化中将绝对值全部改为相对值
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`<div class="ac-game-playground"></div>`);
this.hide();
this.root.$ac_game.append(this.$playground);
this.start();
}
get_random_color() {
let colors = ["blue", "red", "pink", "grey", "green"];
return colors[Math.floor(Math.random() * 5)];
}
start() {
let outer = this;
$(window).resize(function() {
outer.resize();
});
}
resize() {
this.width = this.$playground.width();
this.height = this.$playground.height();
let unit = Math.min(this.width / 16, this.height / 9);
this.width = unit * 16;
this.height = unit * 9;
this.scale = this.height;
if (this.game_map) this.game_map.resize();
}
show() {
this.$playground.show();
this.resize();
this.width = this.$playground.width();
this.height = this.$playground.height();
this.game_map = new GameMap(this);
this.players = [];
this.players.push(new Player(this, this.width / 2 / this.scale, 0.5, 0.05, "white", 0.15, true));
for (let i = 0; i < 5; i ++ ) {
this.players.push(new Player(this, this.width / 2 / this.scale, 0.5, 0.05, this.get_random_color(), 0.15, false));
}
}
hide() {
this.$playground.hide();
}
}
acapp/game/static/js/src/playground/player/zbase.js
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.vx = 0;
this.vy = 0;
this.damage_x = 0;
this.damage_y = 0;
this.damage_speed = 0;
this.move_length = 0;
this.radius = radius;
this.color = color;
this.speed = speed;
this.is_me = is_me;
this.eps = 0.01;
this.friction = 0.9;
this.spent_time = 0;
console.log(color);
this.cur_skill = null;
if (this.is_me) {
this.img = new Image();
this.img.src = this.playground.root.settings.photo;
}
}
start() {
if (this.is_me) {
this.add_listening_events();
} else {
let tx = Math.random() * this.playground.width / this.playground.scale;
let ty = Math.random() * this.playground.height / this.playground.scale;
this.move_to(tx, ty);
}
}
add_listening_events() {
let outer = this;
this.playground.game_map.$canvas.on("contextmenu", function() {
return false;
});
this.playground.game_map.$canvas.mousedown(function(e) {
const rect = outer.ctx.canvas.getBoundingClientRect();
if (e.which === 3) {
outer.move_to((e.clientX - rect.left) / outer.playground.scale, (e.clientY - rect.top) / outer.playground.scale);
} else if (e.which === 1) {
if (outer.cur_skill === "fireball") {
outer.shoot_fireball((e.clientX - rect.left) / outer.playground.scale, (e.clientY - rect.top) / outer.playground.scale);
}
outer.cur_skill = null;
}
});
$(window).keydown(function(e) {
if (e.which === 81) {
outer.cur_skill = "fireball";
return false;
}
});
}
shoot_fireball(tx, ty) {
let x = this.x, y = this.y;
let radius = 0.01;
let angle = Math.atan2(ty - this.y, tx - this.x);
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = "orange";
let speed = 0.5;
let move_length = 1;
new FireBall(this.playground, this, x, y, radius, vx, vy, color, speed, move_length, 0.01);
}
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
move_to(tx, ty) {
this.move_length = this.get_dist(this.x, this.y, tx, ty);
let angle = Math.atan2(ty - this.y, tx - this.x);
this.vx = Math.cos(angle);
this.vy = Math.sin(angle);
}
is_attacked(angle, damage) {
for (let i = 0; i < 20 + Math.random() * 10; i ++ ) {
let x = this.x, y = this.y;
let radius = this.radius * Math.random() * 0.1;
let angle = Math.PI * 2 * Math.random();
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = this.color;
let speed = this.speed * 10;
let move_length = this.radius * Math.random() * 5;
new Particle(this.playground, x, y, radius, vx, vy, color, speed, move_length);
}
this.radius -= damage;
if (this.radius < this.eps) {
this.destroy();
return false;
}
this.damage_x = Math.cos(angle);
this.damage_y = Math.sin(angle);
this.damage_speed = damage * 100;
this.speed *= 0.8;
}
update() {
this.update_move();
this.render();
}
update_move() {
this.spent_time += this.timedelta / 1000;
if (!this.is_me && this.spent_time > 4 && Math.random() < 1 / 300.0) {
let player = this.playground.players[Math.floor(Math.random() * this.playground.players.length)];
let tx = player.x + player.speed * this.vx * this.timedelta / 1000 * 0.3;
let ty = player.y + player.speed * this.vy * this.timedelta / 1000 * 0.3;
this.shoot_fireball(tx, ty);
}
if (this.damage_speed > this.eps) {
this.vx = this.vy = 0;
this.move_length = 0;
this.x += this.damage_x * this.damage_speed * this.timedelta / 1000;
this.y += this.damage_y * this.damage_speed * this.timedelta / 1000;
this.damage_speed *= this.friction;
} else {
if (this.move_length < this.eps) {
this.move_length = 0;
this.vx = this.vy = 0;
if (!this.is_me) {
let tx = Math.random() * this.playground.width / this.playground.scale;
let ty = Math.random() * this.playground.height / this.playground.scale;
this.move_to(tx, ty);
}
} else {
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.move_length -= moved;
}
}
}
render() {
let scale = this.playground.scale;
if (this.is_me) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.stroke();
this.ctx.clip();
this.ctx.drawImage(this.img, (this.x - this.radius) * scale, (this.y - this.radius) * scale, this.radius * 2 * scale, this.radius * 2 * scale);
this.ctx.restore();
} else {
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
on_destroy() {
for (let i = 0; i < this.playground.players.length; i ++ ) {
if (this.playground.players[i] === this) {
this.playground.players.splice(i, 1);
}
}
}
}
acapp/game/static/js/src/playground/skill/fireball/zbase.js
class FireBall extends AcGameObject {
constructor(playground, player, x, y, radius, vx, vy, color, speed, move_length, damage) {
super();
this.playground = playground;
this.player = player;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = radius;
this.color = color;
this.speed = speed;
this.move_length = move_length;
this.damage = damage;
this.eps = 0.01;
}
start() {
}
update() {
if (this.move_length < this.eps) {
this.destroy();
return false;
}
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.move_length -= moved;
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);
}
}
this.render();
}
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
is_collision(player) {
let distance = this.get_dist(this.x, this.y, player.x, player.y);
if (distance < this.radius + player.radius)
return true;
return false;
}
attack(player) {
let angle = Math.atan2(player.y - this.y, player.x - this.x);
player.is_attacked(angle, this.damage);
this.destroy();
}
render() {
let scale = this.playground.scale;
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
acapp/game/static/js/src/playground/particle/zbase.js
class Particle extends AcGameObject {
constructor(playground, x, y, radius, vx, vy, 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.vx = vx;
this.vy = vy;
this.color = color;
this.speed = speed;
this.move_length = move_length;
this.friction = 0.9;
this.eps = 0.01;
}
start() {
}
update() {
if (this.move_length < this.eps || this.speed < this.eps) {
this.destroy();
return false;
}
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.speed *= this.friction;
this.move_length -= moved;
this.render();
}
render() {
let scale = this.playground.scale;
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
3.增加联机对战模式
在menu中实现多人模式按钮逻辑: acapp/game/static/js/src/menu/zbase.js
class AcGameMenu {
constructor(root) {
this.root = root;
this.$menu = $(`
<div class="ac-game-menu">
<div class="ac-game-menu-field">
<div class="ac-game-menu-field-item ac-game-menu-field-item-single-mode">
单人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-multi-mode">
多人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-settings">
退出
</div>
</div>
</div>
`);
this.$menu.hide();
this.root.$ac_game.append(this.$menu);
this.$single_mode = this.$menu.find('.ac-game-menu-field-item-single-mode');
this.$multi_mode = this.$menu.find('.ac-game-menu-field-item-multi-mode');
this.$settings = this.$menu.find('.ac-game-menu-field-item-settings');
this.start();
}
start() {
this.add_listening_events();
}
add_listening_events() {
let outer = this;
this.$single_mode.click(function(){
outer.hide();
outer.root.playground.show("single mode");
});
this.$multi_mode.click(function(){
outer.hide();
outer.root.playground.show("multi mode");
});
this.$settings.click(function(){
outer.root.settings.logout_on_remote();
});
}
show() {
this.$menu.show();
}
hide() {
this.$menu.hide();
}
}
更改playground: acapp/game/static/js/src/playground/zbase.js
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`<div class="ac-game-playground"></div>`);
this.hide();
this.root.$ac_game.append(this.$playground);
this.start();
}
get_random_color() {
let colors = ["blue", "red", "pink", "grey", "green"];
return colors[Math.floor(Math.random() * 5)];
}
start() {
let outer = this;
$(window).resize(function() {
outer.resize();
});
}
resize() {
this.width = this.$playground.width();
this.height = this.$playground.height();
let unit = Math.min(this.width / 16, this.height / 9);
this.width = unit * 16;
this.height = unit * 9;
this.scale = this.height;
if (this.game_map) this.game_map.resize();
}
show(mode) {
let outer = this;
this.$playground.show();
this.width = this.$playground.width();
this.height = this.$playground.height();
this.game_map = new GameMap(this);
this.resize();
this.players = [];
this.players.push(new Player(this, this.width / 2 / this.scale, 0.5, 0.05, "white", 0.15, "me", this.root.settings.username, this.root.settings.photo));
if (mode === "single mode") {
for (let i = 0; i < 5; i ++ ) {
this.players.push(new Player(this, this.width / 2 / this.scale, 0.5, 0.05, this.get_random_color(), 0.15, "robot"));
}
} else if (mode === "multi mode") {
this.mps = new MultiPlayerSocket(this);
this.mps.uuid = this.players[0].uuid;
this.mps.ws.onopen = function() {
outer.mps.send_create_player(outer.root.settings.username, outer.root.settings.photo);
};
}
}
hide() {
this.$playground.hide();
}
}
更改player acapp/game/static/js/src/playground/player/zbase.js
class Player extends AcGameObject {
constructor(playground, x, y, radius, color, speed, character, username, photo) {
console.log(character, username, photo);
super();
this.playground = playground;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.damage_x = 0;
this.damage_y = 0;
this.damage_speed = 0;
this.move_length = 0;
this.radius = radius;
this.color = color;
this.speed = speed;
this.character = character;
this.username = username;
this.photo = photo;
this.eps = 0.01;
this.friction = 0.9;
this.spent_time = 0;
this.cur_skill = null;
if (this.character !== "robot") {
this.img = new Image();
this.img.src = this.photo;
}
}
start() {
if (this.character === "me") {
this.add_listening_events();
} else if (this.character === "robot") {
let tx = Math.random() * this.playground.width / this.playground.scale;
let ty = Math.random() * this.playground.height / this.playground.scale;
this.move_to(tx, ty);
}
}
add_listening_events() {
let outer = this;
this.playground.game_map.$canvas.on("contextmenu", function() {
return false;
});
this.playground.game_map.$canvas.mousedown(function(e) {
const rect = outer.ctx.canvas.getBoundingClientRect();
if (e.which === 3) {
outer.move_to((e.clientX - rect.left) / outer.playground.scale, (e.clientY - rect.top) / outer.playground.scale);
} else if (e.which === 1) {
if (outer.cur_skill === "fireball") {
outer.shoot_fireball((e.clientX - rect.left) / outer.playground.scale, (e.clientY - rect.top) / outer.playground.scale);
}
outer.cur_skill = null;
}
});
$(window).keydown(function(e) {
if (e.which === 81) {
outer.cur_skill = "fireball";
return false;
}
});
}
shoot_fireball(tx, ty) {
let x = this.x, y = this.y;
let radius = 0.01;
let angle = Math.atan2(ty - this.y, tx - this.x);
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = "orange";
let speed = 0.5;
let move_length = 1;
new FireBall(this.playground, this, x, y, radius, vx, vy, color, speed, move_length, 0.01);
}
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
move_to(tx, ty) {
this.move_length = this.get_dist(this.x, this.y, tx, ty);
let angle = Math.atan2(ty - this.y, tx - this.x);
this.vx = Math.cos(angle);
this.vy = Math.sin(angle);
}
is_attacked(angle, damage) {
for (let i = 0; i < 20 + Math.random() * 10; i ++ ) {
let x = this.x, y = this.y;
let radius = this.radius * Math.random() * 0.1;
let angle = Math.PI * 2 * Math.random();
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = this.color;
let speed = this.speed * 10;
let move_length = this.radius * Math.random() * 5;
new Particle(this.playground, x, y, radius, vx, vy, color, speed, move_length);
}
this.radius -= damage;
if (this.radius < this.eps) {
this.destroy();
return false;
}
this.damage_x = Math.cos(angle);
this.damage_y = Math.sin(angle);
this.damage_speed = damage * 100;
this.speed *= 0.8;
}
update() {
this.update_move();
this.render();
}
update_move() {
this.spent_time += this.timedelta / 1000;
if (this.character === "robot" && this.spent_time > 4 && Math.random() < 1 / 300.0) {
let player = this.playground.players[Math.floor(Math.random() * this.playground.players.length)];
let tx = player.x + player.speed * this.vx * this.timedelta / 1000 * 0.3;
let ty = player.y + player.speed * this.vy * this.timedelta / 1000 * 0.3;
this.shoot_fireball(tx, ty);
}
if (this.damage_speed > this.eps) {
this.vx = this.vy = 0;
this.move_length = 0;
this.x += this.damage_x * this.damage_speed * this.timedelta / 1000;
this.y += this.damage_y * this.damage_speed * this.timedelta / 1000;
this.damage_speed *= this.friction;
} else {
if (this.move_length < this.eps) {
this.move_length = 0;
this.vx = this.vy = 0;
if (this.character === "robot") {
let tx = Math.random() * this.playground.width / this.playground.scale;
let ty = Math.random() * this.playground.height / this.playground.scale;
this.move_to(tx, ty);
}
} else {
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.move_length -= moved;
}
}
}
render() {
let scale = this.playground.scale;
if (this.character !== "robot") {
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.stroke();
this.ctx.clip();
this.ctx.drawImage(this.img, (this.x - this.radius) * scale, (this.y - this.radius) * scale, this.radius * 2 * scale, this.radius * 2 * scale);
this.ctx.restore();
} else {
this.ctx.beginPath();
this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
on_destroy() {
for (let i = 0; i < this.playground.players.length; i ++ ) {
if (this.playground.players[i] === this) {
this.playground.players.splice(i, 1);
}
}
}
}
|