game: add: vector colision (Not work)
This commit is contained in:
parent
ab325ae489
commit
d332ef8103
@ -7,33 +7,37 @@ class Ball
|
||||
/**
|
||||
*
|
||||
* @param {Game} game
|
||||
* @param {Number} position_x
|
||||
* @param {Number} position_y
|
||||
* @param {Number} velocity_x
|
||||
* @param {Number} velocity_y
|
||||
* @param {Point} position
|
||||
* @param {Number} angle
|
||||
* @param {Number} speed
|
||||
* @param {Number} size
|
||||
*/
|
||||
constructor(game, position_x, position_y, velocity_x, velocity_y)
|
||||
constructor(game, size, position, angle, speed)
|
||||
{
|
||||
/**
|
||||
* @type {Game}
|
||||
*/
|
||||
this.game = game;
|
||||
|
||||
/**
|
||||
* @type {Point}
|
||||
*/
|
||||
this.position = position === undefined ? new Point() : position;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.position_x = position_x;
|
||||
this.size = size;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.position_y = position_y;
|
||||
this.angle = angle;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.velocity_x = velocity_x;
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.velocity_y = velocity_y;
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,40 +46,35 @@ class Ball
|
||||
*/
|
||||
draw(ctx)
|
||||
{
|
||||
ctx.rect(this.position_x, this.position_y, this.game.config.ball_size, this.game.config.ball_size);
|
||||
ctx.rect(this.position.x - this.size / 2, this.position.y - this.size / 2, this.game.config.ball_size, this.game.config.ball_size);
|
||||
}
|
||||
|
||||
render()
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
render(ctx)
|
||||
{
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
let new_pos_x = this.position_x + this.velocity_x * this.game.time.deltaTime();
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
let new_pos_y = position_y + this.velocity_y * this.game.time.deltaTime();
|
||||
let distance = this.speed * (this.game.time.deltaTime() / 1000)
|
||||
|
||||
if (this._collision(this.position_x, this.position_y, new_pos_x, new_pos_y))
|
||||
{
|
||||
this.velocity_x = -this.velocity_x;
|
||||
this.velocity_y = -this.velocity_y;
|
||||
new_pos_x = this.position_x + this.velocity_x * this.game.time.deltaTime();
|
||||
new_pos_y = this.position_y + this.velocity_y * this.game.time.deltaTime();
|
||||
}
|
||||
this.position_x = new_pos_x
|
||||
this.position_y = new_pos_y
|
||||
let angle_radian = this.angle * Math.PI / 180
|
||||
|
||||
this.velocity_x = this.velocity_x + this.game.config.ball_speed_inc;
|
||||
this.velocity_y = this.velocity_y + this.game.config.ball_speed_inc;
|
||||
console.log(angle_radian)
|
||||
|
||||
this.position.x = this.position.x + distance * Math.cos(angle_radian);
|
||||
this.position.y = this.position.y + distance * Math.sin(angle_radian);
|
||||
|
||||
this.draw(ctx);
|
||||
}
|
||||
|
||||
update (position_x, position_y, velocity_x, velocity_y)
|
||||
from_json (data)
|
||||
{
|
||||
this.position_x = position_x;
|
||||
this.position_y = position_y;
|
||||
this.velocity_x = velocity_x;
|
||||
this.velocity_y = velocity_y;
|
||||
this.position = this.position.from_json(data.position);
|
||||
this.size = data.size;
|
||||
this.angle = data.angle;
|
||||
this.speed = data.speed;
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { GameConfig } from "./GameConfig.js"
|
||||
import { Player } from "./Player.js";
|
||||
import { Time } from "./Time.js";
|
||||
import { Wall } from "./Wall.js";
|
||||
import { Client } from "../client.js";
|
||||
|
||||
class Game
|
||||
{
|
||||
@ -21,7 +22,7 @@ class Game
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
* @returns {Promise<Number>}
|
||||
*/
|
||||
async init()
|
||||
{
|
||||
@ -32,23 +33,52 @@ class Game
|
||||
|
||||
let response_data = await response.json();
|
||||
|
||||
/**
|
||||
* @type {[Number]}
|
||||
*/
|
||||
this.players_id = response_data.players_id;
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
this.state = response_data.state;
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.started = response_data.started;
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.finished = response_data.finished;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.winner_id = this.finished ? response_data.winner_id : undefined;
|
||||
|
||||
if (this.finished === true)
|
||||
return 0;
|
||||
|
||||
/**
|
||||
* @type {GameConfig}
|
||||
*/
|
||||
this.config = new GameConfig(this.client);
|
||||
|
||||
|
||||
let ret = await this.config.init();
|
||||
if (ret !== 0)
|
||||
return ret;
|
||||
|
||||
/**
|
||||
* @type {Time}
|
||||
*/
|
||||
this.time = new Time();
|
||||
this.last_pos = null
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._inited = false;
|
||||
|
||||
return 0;
|
||||
@ -72,11 +102,11 @@ class Game
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
draw(ctx)
|
||||
render(ctx)
|
||||
{
|
||||
ctx.clearRect(0, 0, this.config.size_x, this.config.size_y);
|
||||
this.draw_sides(ctx);
|
||||
this.ball.draw(ctx);
|
||||
this.ball.render(ctx);
|
||||
}
|
||||
|
||||
_send(data)
|
||||
@ -90,50 +120,25 @@ class Game
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} position
|
||||
* @param {Number} time
|
||||
*/
|
||||
_send_paddle_position(position, time)
|
||||
{
|
||||
if (this.last_pos !== null && this.last_pos.time >= time)
|
||||
return;
|
||||
|
||||
this.last_pos = {"time": time, "position": position};
|
||||
|
||||
this._send({"detail": "update_my_paddle_pos", ...this.last_pos});
|
||||
}
|
||||
|
||||
_receive_player_join(player_data)
|
||||
{
|
||||
console.log(player_data)
|
||||
let index = this.players.indexOf((player) => player.id === player_data.user_id);
|
||||
|
||||
this.players[index].is_connected = true;
|
||||
}
|
||||
|
||||
_receive_player_leave(player_data)
|
||||
{
|
||||
let index = this.players.indexOf((player) => player.id === player_data.user_id);
|
||||
|
||||
this.players[index].is_connected = false;
|
||||
}
|
||||
|
||||
_receive_update_ball(data)
|
||||
{
|
||||
this.ball.position_x = data.position_x
|
||||
this.ball.position_y = data.position_y
|
||||
this.ball.position_x = data.position_x
|
||||
this.ball.position_x = data.position_x
|
||||
this._send({"detail": "update_my_paddle_pos", ...{"time": time, "position": position}});
|
||||
}
|
||||
|
||||
_receive_update_paddle(data)
|
||||
{
|
||||
let player = this.players.find((player) => player.id === data.user_id);
|
||||
|
||||
if (player === null)
|
||||
{
|
||||
this._receive_player_join(data);
|
||||
return;
|
||||
}
|
||||
player.is_connected = data.is_connected;
|
||||
player.update_pos(data.position.position, data.position.time);
|
||||
player.from_json(data);
|
||||
}
|
||||
|
||||
_receive_ball(data)
|
||||
{
|
||||
this.ball.from_json(data);
|
||||
}
|
||||
|
||||
_receive(data)
|
||||
@ -141,26 +146,30 @@ class Game
|
||||
if (data.detail === "update_paddle")
|
||||
this._receive_update_paddle(data);
|
||||
else if (data.detail === "update_ball")
|
||||
this._receive_update_ball(data);
|
||||
this._receive_ball(data)
|
||||
else if (data.detail === "init_game")
|
||||
this._init_game(data)
|
||||
else if (data.detail === "player_join")
|
||||
this._receive_player_join(data)
|
||||
else if (data.detail === "player_leave")
|
||||
this._receive_player_leave(data)
|
||||
this._init_game(data);
|
||||
}
|
||||
|
||||
_init_game(data)
|
||||
{
|
||||
const ball_data = data.ball;
|
||||
this.ball = new Ball(this, ball_data.position_x, ball_data.position_y, ball_data.velocity_x, ball_data.velocity_y);
|
||||
/**
|
||||
* @type {Ball}
|
||||
*/
|
||||
this.ball = (new Ball(this)).from_json(data.ball)
|
||||
|
||||
/**
|
||||
* @type {[Wall]}
|
||||
*/
|
||||
this.walls = [];
|
||||
const walls_data = data.walls;
|
||||
walls_data.forEach((wall_data) => {
|
||||
this.walls.push(new Wall().from_json(wall_data));
|
||||
});
|
||||
|
||||
/**
|
||||
* @type {[Player]}
|
||||
*/
|
||||
this.players = []
|
||||
const players_data = data.players;
|
||||
players_data.forEach((player_data) => {
|
||||
|
@ -24,14 +24,17 @@ class GameConfig
|
||||
* @type {Number}
|
||||
*/
|
||||
this.size_x = response_data.MAP_SIZE_X;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.size_y = response_data.MAP_SIZE_Y;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.center_x = this.size_x / 2;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
@ -63,10 +66,12 @@ class GameConfig
|
||||
* @type {Number}
|
||||
*/
|
||||
this.ball_size = response_data.BALL_SIZE;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.ball_spawn_x = this.center_x;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
|
@ -17,7 +17,9 @@ class Time
|
||||
|
||||
deltaTime()
|
||||
{
|
||||
return (this._current_frame - this._last_frame) !== NaN ? this._current_frame - this._last_frame : 0;
|
||||
if (this._last_frame === undefined)
|
||||
return 0;
|
||||
return (this._current_frame - this._last_frame);
|
||||
}
|
||||
|
||||
deltaTimeSecond()
|
||||
|
@ -26,7 +26,7 @@ export default class extends AbstractView
|
||||
this.keys_pressed.push(event.key);
|
||||
}
|
||||
|
||||
draw()
|
||||
render_game()
|
||||
{
|
||||
const canva = document.getElementById('canva');
|
||||
|
||||
@ -40,22 +40,23 @@ export default class extends AbstractView
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
this.game.draw(ctx);
|
||||
this.game.render(ctx);
|
||||
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = 10;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
render_game()
|
||||
render()
|
||||
{
|
||||
let loop_id = setInterval(() => {
|
||||
if (this.game === undefined)
|
||||
clearInterval(loop_id);
|
||||
if (this.my_player)
|
||||
this.my_player.update_paddle(this.keys_pressed);
|
||||
this.draw();
|
||||
this.render_game();
|
||||
this.game?.time.new_frame();
|
||||
//clearInterval(loop_id);
|
||||
// 1 sec fps
|
||||
}, 1000 / 60);
|
||||
}
|
||||
@ -99,7 +100,7 @@ export default class extends AbstractView
|
||||
|
||||
this.register_key()
|
||||
|
||||
this.render_game();
|
||||
this.render();
|
||||
}
|
||||
|
||||
async update_game_state()
|
||||
|
@ -12,7 +12,7 @@ MAP_CENTER_Y = MAP_SIZE_Y / 2
|
||||
WALL_RATIO = 1
|
||||
|
||||
BALL_SPEED_INC = 1
|
||||
BALL_SPEED_START = 1
|
||||
BALL_SPEED_START = 170
|
||||
BALL_SIZE = 4
|
||||
BALL_SPAWN_POS_X = MAP_SIZE_X / 2
|
||||
BALL_SPAWN_POS_Y = MAP_SIZE_Y / 2
|
||||
|
@ -1,23 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import config
|
||||
|
||||
from .Point import Point
|
||||
|
||||
import math
|
||||
class Ball:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.postion_x: float = config.BALL_SPAWN_POS_X
|
||||
self.postion_y: float = config.BALL_SPAWN_POS_Y
|
||||
self.velocity_x: float = config.BALL_SPEED_START
|
||||
self.velocity_y: float = config.BALL_SPEED_START
|
||||
self.size: float = config.BALL_SIZE
|
||||
self.position: Point = Point(config.BALL_SPAWN_POS_X + self.size / 2, config.BALL_SPAWN_POS_Y + self.size / 2)
|
||||
self.angle: float = math.pi * 0
|
||||
self.speed: float = config.BALL_SPEED_START
|
||||
|
||||
def to_dict(self):
|
||||
|
||||
data: dict = {
|
||||
"size": self.size,
|
||||
"position_x": self.postion_x,
|
||||
"position_y": self.postion_y,
|
||||
"velocity_x": self.velocity_x,
|
||||
"velocity_y": self.velocity_y,
|
||||
"speed": self.speed,
|
||||
"position": self.position.to_dict(),
|
||||
"angle": self.angle,
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Ball(size: {self.size}, speed: {self.speed}, director_coefficient: {self.director_coefficient}, ordinate_at_origin: {self.ordinate_at_origin}, position: {self.position})"
|
@ -44,8 +44,8 @@ class Game(AbstractRoom):
|
||||
|
||||
angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / nb_sides)
|
||||
|
||||
x: float = config.MAP_CENTER_X + radius * math.cos(angle)
|
||||
y: float = config.MAP_CENTER_Y + radius * math.sin(angle)
|
||||
x: float = round(config.MAP_CENTER_X + radius * math.cos(angle))
|
||||
y: float = round(config.MAP_CENTER_Y + radius * math.sin(angle))
|
||||
|
||||
polygon.append(Point(x, y))
|
||||
|
||||
|
@ -1,11 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
from math import dist
|
||||
class Point:
|
||||
|
||||
def __init__(self, x: float, y: float) -> None:
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Point(x: {self.x}, y: {self.y})"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Point(x: {self.x}, y: {self.x})"
|
||||
|
||||
def __eq__(self, __value: object) -> bool:
|
||||
return (self.x == __value.x and self.y == __value.y)
|
||||
|
||||
def distance(self, point: Point):
|
||||
return dist((point.x, point.y), (self.x, self.y))
|
||||
|
||||
def copy(self):
|
||||
|
||||
return Point(self.x, self.y)
|
||||
|
||||
def to_dict(self):
|
||||
|
||||
data: dict[str: float] = {
|
||||
|
@ -1,12 +1,20 @@
|
||||
|
||||
from .Point import Point
|
||||
|
||||
import math
|
||||
class Segment:
|
||||
|
||||
def __init__(self, start: Point, stop: Point) -> None:
|
||||
self.start: Point = start
|
||||
self.stop: Point = stop
|
||||
|
||||
self.length: float = math.dist((self.start.x, self.start.y), (self.stop.x, self.stop.y))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Segment(start: {self.start}, stop: {self.stop})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Segment(start: {self.start}, stop: {self.stop})"
|
||||
|
||||
def to_dict(self):
|
||||
|
||||
data: dict[str: dict] = {
|
||||
|
35
games/objects/Vector.py
Normal file
35
games/objects/Vector.py
Normal file
@ -0,0 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from .Point import Point
|
||||
|
||||
class Vector:
|
||||
|
||||
def __init__(self, x: float, y: float) -> None:
|
||||
self.norm: float = math.dist((0, 0), (x, y))
|
||||
self.x: float = x
|
||||
self.y: float = y
|
||||
|
||||
def __truediv__(self, denominator: float):
|
||||
return Vector(self.x / denominator, self.y / denominator)
|
||||
|
||||
def angle(self, vector: Vector):
|
||||
scalar_product: float = self.scalar(vector)
|
||||
if (scalar_product is None):
|
||||
return None
|
||||
cos: float = scalar_product / (vector.norm * self.norm)
|
||||
|
||||
angle: float = math.acos(cos)
|
||||
|
||||
return angle
|
||||
|
||||
def scalar(self, vector: Vector):
|
||||
return self.x * vector.x + vector.y * self.y
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Vector(x: {self.x}, y: {self.y}, norme: {self.norm})"
|
||||
|
||||
def __eq__(self, __value: Vector) -> bool:
|
||||
return (self.x == __value.x and
|
||||
self.x == __value.x and
|
||||
self.norm == __value.norm)
|
118
games/routine.py
118
games/routine.py
@ -9,73 +9,99 @@ if TYPE_CHECKING:
|
||||
from .objects.Ball import Ball
|
||||
|
||||
from .objects.Point import Point
|
||||
from .objects.Vector import Point
|
||||
from .objects.Segment import Segment
|
||||
from .objects.Vector import Vector
|
||||
|
||||
from . import config
|
||||
|
||||
import math
|
||||
import asyncio
|
||||
|
||||
from asgiref.sync import SyncToAsync
|
||||
|
||||
from time import sleep
|
||||
|
||||
#see the video to understand the algorithme
|
||||
#https://www.youtube.com/watch?v=KOYoMYWUTEo
|
||||
def determine_director_coefficient(segment: Segment):
|
||||
return ((segment.start.y - segment.stop.y) / (segment.start.x - segment.stop.x))
|
||||
def get_sign(num: float):
|
||||
return 1 if num >= 0 else -1
|
||||
|
||||
def determine_ordinate_at_origin(point: Point, director_cofficient: float):
|
||||
return point.y - point.x * director_cofficient
|
||||
def get_impact_point(segments: list[Segment], ball: Ball):
|
||||
|
||||
def determine_intersection(director_coefficient1: float, ordinate_at_origin1: float, director_coefficient2: float, ordinate_at_origin2: float):
|
||||
if (director_coefficient1 == director_coefficient2):
|
||||
return None
|
||||
return (ordinate_at_origin1 + ordinate_at_origin2) / (director_coefficient1 + director_coefficient2)
|
||||
angle_radian: float = ball.angle * math.pi / 180
|
||||
|
||||
def determine_intersections(ball: Ball, segments: list[Segment]):
|
||||
|
||||
intersections: list[Point] = []
|
||||
direction_vector: Vector = Vector(math.cos(angle_radian), math.sin(angle_radian))
|
||||
|
||||
x: float = ball.position.x
|
||||
if (direction_vector.x > 0):
|
||||
x = x + ball.size / 2
|
||||
elif (direction_vector.x < 0):
|
||||
x = x - ball.size / 2
|
||||
|
||||
y: float = ball.position.y
|
||||
if (direction_vector.y > 0):
|
||||
y = y + ball.size / 2
|
||||
elif (direction_vector.y < 0):
|
||||
y = y - ball.size / 2
|
||||
|
||||
position: Point = Point(x, y)
|
||||
|
||||
for segment in segments:
|
||||
|
||||
# form m * x + p
|
||||
m: float = determine_director_coefficient(segment)
|
||||
p: float = determine_ordinate_at_origin(segment.start, m)
|
||||
segment_vector: Vector = Vector(segment.start.x - segment.stop.x, segment.start.y - segment.stop.y)
|
||||
segment_vector_unit = segment_vector / segment_vector.norm
|
||||
|
||||
x: float = determine_intersection(m, p, ball.velocity_y, 0)
|
||||
scalar: float = segment_vector_unit.scalar(direction_vector)
|
||||
|
||||
if (x is None):
|
||||
continue
|
||||
|
||||
y: float = m * x + p
|
||||
|
||||
intersections.append(Point(x, y))
|
||||
if (scalar < 0.01):
|
||||
continue
|
||||
|
||||
return intersections
|
||||
print(segment_vector, segment_vector_unit, direction_vector)
|
||||
|
||||
distance: float = scalar * segment_vector.norm / 2
|
||||
|
||||
impact_x: float = position.x + distance * direction_vector.x
|
||||
|
||||
impact_y: float = position.y + distance * direction_vector.y
|
||||
|
||||
impact: Point = Point(impact_x, impact_y)
|
||||
|
||||
print("impact", impact)
|
||||
|
||||
return impact
|
||||
|
||||
async def update_ball(game: Game, impact: Point):
|
||||
|
||||
distance: float = impact.distance(game.ball.position) - game.ball.size / 2
|
||||
|
||||
time_before_impact: float = distance / game.ball.speed
|
||||
|
||||
await asyncio.sleep(time_before_impact)
|
||||
|
||||
game.ball.angle = game.ball.angle + 180
|
||||
|
||||
game.ball.position = impact
|
||||
|
||||
await SyncToAsync(game.broadcast)("update_ball", game.ball.to_dict())
|
||||
|
||||
async def render(game: Game):
|
||||
|
||||
while True:
|
||||
|
||||
segments: list[Segment] = [player.rail for player in game.players] + [wall.rail for wall in game.walls]
|
||||
|
||||
impact = get_impact_point(segments, game.ball)
|
||||
|
||||
await update_ball(game, impact)
|
||||
|
||||
def determine_distance_between_ball_and_wall(ball: Ball, segments: list[Segment]):
|
||||
|
||||
intersections: list[Point] = determine_intersections(ball, segments)
|
||||
|
||||
distances = list(map(math.dist, intersections))
|
||||
|
||||
return min(distances)
|
||||
|
||||
def render(ball: Ball, game: Game):
|
||||
|
||||
segments: list[Segment] = [player.rail for player in game.players]
|
||||
|
||||
print(determine_distance_between_ball_and_wall(ball))
|
||||
|
||||
def routine(game: Game):
|
||||
|
||||
|
||||
asyncio.run(render(game))
|
||||
|
||||
while True:
|
||||
for player in game._updated_players:
|
||||
game.broadcast("update_paddle", player.to_dict(), [player])
|
||||
|
||||
|
||||
game._updated_players.clear()
|
||||
|
||||
if (game.started):
|
||||
game.ball.postion_x = game.ball.postion_x + game.ball.velocity_x
|
||||
game.ball.postion_y = game.ball.postion_y + game.ball.velocity_y
|
||||
|
||||
game.broadcast("update_ball", game.ball.to_dict())
|
||||
|
||||
|
||||
sleep(1 / config.SERVER_TPS)
|
Loading…
Reference in New Issue
Block a user