game: add: vector colision (Not work)

This commit is contained in:
starnakin 2024-02-01 13:18:11 +01:00 committed by AdrienLSH
parent 3e4d52c645
commit 4b092b31bb
12 changed files with 260 additions and 153 deletions

View File

@ -7,33 +7,37 @@ class Ball
/** /**
* *
* @param {Game} game * @param {Game} game
* @param {Number} position_x * @param {Point} position
* @param {Number} position_y * @param {Number} angle
* @param {Number} velocity_x * @param {Number} speed
* @param {Number} velocity_y * @param {Number} size
*/ */
constructor(game, position_x, position_y, velocity_x, velocity_y) constructor(game, size, position, angle, speed)
{ {
/** /**
* @type {Game} * @type {Game}
*/ */
this.game = game; this.game = game;
/**
* @type {Point}
*/
this.position = position === undefined ? new Point() : position;
/** /**
* @type {Number} * @type {Number}
*/ */
this.position_x = position_x; this.size = size;
/** /**
* @type {Number} * @type {Number}
*/ */
this.position_y = position_y; this.angle = angle;
/** /**
* @type {Number} * @type {Number}
*/ */
this.velocity_x = velocity_x; this.speed = speed;
/**
* @type {Number}
*/
this.velocity_y = velocity_y;
} }
/** /**
@ -42,40 +46,35 @@ class Ball
*/ */
draw(ctx) 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)
{ {
/** let distance = this.speed * (this.game.time.deltaTime() / 1000)
* @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();
if (this._collision(this.position_x, this.position_y, new_pos_x, new_pos_y)) let angle_radian = this.angle * Math.PI / 180
{
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
this.velocity_x = this.velocity_x + this.game.config.ball_speed_inc; console.log(angle_radian)
this.velocity_y = this.velocity_y + this.game.config.ball_speed_inc;
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 = this.position.from_json(data.position);
this.position_y = position_y; this.size = data.size;
this.velocity_x = velocity_x; this.angle = data.angle;
this.velocity_y = velocity_y; this.speed = data.speed;
return this
} }
} }

View File

@ -4,6 +4,7 @@ import { GameConfig } from "./GameConfig.js"
import { Player } from "./Player.js"; import { Player } from "./Player.js";
import { Time } from "./Time.js"; import { Time } from "./Time.js";
import { Wall } from "./Wall.js"; import { Wall } from "./Wall.js";
import { Client } from "../client.js";
class Game class Game
{ {
@ -21,7 +22,7 @@ class Game
/** /**
* *
* @returns {Number} * @returns {Promise<Number>}
*/ */
async init() async init()
{ {
@ -32,23 +33,52 @@ class Game
let response_data = await response.json(); let response_data = await response.json();
/**
* @type {[Number]}
*/
this.players_id = response_data.players_id; this.players_id = response_data.players_id;
/**
* @type {String}
*/
this.state = response_data.state; this.state = response_data.state;
/**
* @type {Boolean}
*/
this.started = response_data.started; this.started = response_data.started;
/**
* @type {Boolean}
*/
this.finished = response_data.finished; this.finished = response_data.finished;
/**
* @type {Number}
*/
this.winner_id = this.finished ? response_data.winner_id : undefined; this.winner_id = this.finished ? response_data.winner_id : undefined;
if (this.finished === true) if (this.finished === true)
return 0; return 0;
/**
* @type {GameConfig}
*/
this.config = new GameConfig(this.client); this.config = new GameConfig(this.client);
let ret = await this.config.init(); let ret = await this.config.init();
if (ret !== 0) if (ret !== 0)
return ret; return ret;
/**
* @type {Time}
*/
this.time = new Time(); this.time = new Time();
this.last_pos = null
/**
* @type {Boolean}
*/
this._inited = false; this._inited = false;
return 0; return 0;
@ -72,11 +102,11 @@ class Game
* *
* @param {CanvasRenderingContext2D} ctx * @param {CanvasRenderingContext2D} ctx
*/ */
draw(ctx) render(ctx)
{ {
ctx.clearRect(0, 0, this.config.size_x, this.config.size_y); ctx.clearRect(0, 0, this.config.size_x, this.config.size_y);
this.draw_sides(ctx); this.draw_sides(ctx);
this.ball.draw(ctx); this.ball.render(ctx);
} }
_send(data) _send(data)
@ -90,50 +120,25 @@ class Game
} }
} }
/**
* @param {Number} position
* @param {Number} time
*/
_send_paddle_position(position, time) _send_paddle_position(position, time)
{ {
if (this.last_pos !== null && this.last_pos.time >= time) this._send({"detail": "update_my_paddle_pos", ...{"time": time, "position": position}});
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
} }
_receive_update_paddle(data) _receive_update_paddle(data)
{ {
let player = this.players.find((player) => player.id === data.user_id); let player = this.players.find((player) => player.id === data.user_id);
if (player === null) player.from_json(data);
{ }
this._receive_player_join(data);
return; _receive_ball(data)
} {
player.is_connected = data.is_connected; this.ball.from_json(data);
player.update_pos(data.position.position, data.position.time);
} }
_receive(data) _receive(data)
@ -141,26 +146,30 @@ class Game
if (data.detail === "update_paddle") if (data.detail === "update_paddle")
this._receive_update_paddle(data); this._receive_update_paddle(data);
else if (data.detail === "update_ball") else if (data.detail === "update_ball")
this._receive_update_ball(data); this._receive_ball(data)
else if (data.detail === "init_game") else if (data.detail === "init_game")
this._init_game(data) 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)
} }
_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 = []; this.walls = [];
const walls_data = data.walls; const walls_data = data.walls;
walls_data.forEach((wall_data) => { walls_data.forEach((wall_data) => {
this.walls.push(new Wall().from_json(wall_data)); this.walls.push(new Wall().from_json(wall_data));
}); });
/**
* @type {[Player]}
*/
this.players = [] this.players = []
const players_data = data.players; const players_data = data.players;
players_data.forEach((player_data) => { players_data.forEach((player_data) => {

View File

@ -24,14 +24,17 @@ class GameConfig
* @type {Number} * @type {Number}
*/ */
this.size_x = response_data.MAP_SIZE_X; this.size_x = response_data.MAP_SIZE_X;
/** /**
* @type {Number} * @type {Number}
*/ */
this.size_y = response_data.MAP_SIZE_Y; this.size_y = response_data.MAP_SIZE_Y;
/** /**
* @type {Number} * @type {Number}
*/ */
this.center_x = this.size_x / 2; this.center_x = this.size_x / 2;
/** /**
* @type {Number} * @type {Number}
*/ */
@ -63,10 +66,12 @@ class GameConfig
* @type {Number} * @type {Number}
*/ */
this.ball_size = response_data.BALL_SIZE; this.ball_size = response_data.BALL_SIZE;
/** /**
* @type {Number} * @type {Number}
*/ */
this.ball_spawn_x = this.center_x; this.ball_spawn_x = this.center_x;
/** /**
* @type {Number} * @type {Number}
*/ */

View File

@ -17,7 +17,9 @@ class Time
deltaTime() 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() deltaTimeSecond()

View File

@ -26,7 +26,7 @@ export default class extends AbstractView
this.keys_pressed.push(event.key); this.keys_pressed.push(event.key);
} }
draw() render_game()
{ {
const canva = document.getElementById('canva'); const canva = document.getElementById('canva');
@ -40,22 +40,23 @@ export default class extends AbstractView
ctx.beginPath(); ctx.beginPath();
this.game.draw(ctx); this.game.render(ctx);
ctx.strokeStyle = "#000000"; ctx.strokeStyle = "#000000";
ctx.lineWidth = 10; ctx.lineWidth = 1;
ctx.stroke(); ctx.stroke();
} }
render_game() render()
{ {
let loop_id = setInterval(() => { let loop_id = setInterval(() => {
if (this.game === undefined) if (this.game === undefined)
clearInterval(loop_id); clearInterval(loop_id);
if (this.my_player) if (this.my_player)
this.my_player.update_paddle(this.keys_pressed); this.my_player.update_paddle(this.keys_pressed);
this.draw(); this.render_game();
this.game?.time.new_frame(); this.game?.time.new_frame();
//clearInterval(loop_id);
// 1 sec fps // 1 sec fps
}, 1000 / 60); }, 1000 / 60);
} }
@ -99,7 +100,7 @@ export default class extends AbstractView
this.register_key() this.register_key()
this.render_game(); this.render();
} }
async update_game_state() async update_game_state()

View File

@ -12,7 +12,7 @@ MAP_CENTER_Y = MAP_SIZE_Y / 2
WALL_RATIO = 1 WALL_RATIO = 1
BALL_SPEED_INC = 1 BALL_SPEED_INC = 1
BALL_SPEED_START = 1 BALL_SPEED_START = 170
BALL_SIZE = 4 BALL_SIZE = 4
BALL_SPAWN_POS_X = MAP_SIZE_X / 2 BALL_SPAWN_POS_X = MAP_SIZE_X / 2
BALL_SPAWN_POS_Y = MAP_SIZE_Y / 2 BALL_SPAWN_POS_Y = MAP_SIZE_Y / 2

View File

@ -1,23 +1,28 @@
from __future__ import annotations
from .. import config from .. import config
from .Point import Point
import math
class Ball: class Ball:
def __init__(self) -> None: 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.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): def to_dict(self):
data: dict = { data: dict = {
"size": self.size, "size": self.size,
"position_x": self.postion_x, "speed": self.speed,
"position_y": self.postion_y, "position": self.position.to_dict(),
"velocity_x": self.velocity_x, "angle": self.angle,
"velocity_y": self.velocity_y,
} }
return data 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})"

View File

@ -44,8 +44,8 @@ class Game(AbstractRoom):
angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / nb_sides) angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / nb_sides)
x: float = config.MAP_CENTER_X + radius * math.cos(angle) x: float = round(config.MAP_CENTER_X + radius * math.cos(angle))
y: float = config.MAP_CENTER_Y + radius * math.sin(angle) y: float = round(config.MAP_CENTER_Y + radius * math.sin(angle))
polygon.append(Point(x, y)) polygon.append(Point(x, y))

View File

@ -1,11 +1,28 @@
from __future__ import annotations
from math import dist
class Point: class Point:
def __init__(self, x: float, y: float) -> None: def __init__(self, x: float, y: float) -> None:
self.x = x self.x = x
self.y = y 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): def to_dict(self):
data: dict[str: float] = { data: dict[str: float] = {

View File

@ -1,11 +1,19 @@
from .Point import Point from .Point import Point
import math
class Segment: class Segment:
def __init__(self, start: Point, stop: Point) -> None: def __init__(self, start: Point, stop: Point) -> None:
self.start: Point = start self.start: Point = start
self.stop: Point = stop 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): def to_dict(self):

35
games/objects/Vector.py Normal file
View 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)

View File

@ -9,73 +9,99 @@ if TYPE_CHECKING:
from .objects.Ball import Ball from .objects.Ball import Ball
from .objects.Point import Point from .objects.Point import Point
from .objects.Vector import Point
from .objects.Segment import Segment from .objects.Segment import Segment
from .objects.Vector import Vector
from . import config from . import config
import math import math
import asyncio
from asgiref.sync import SyncToAsync
from time import sleep from time import sleep
#see the video to understand the algorithme def get_sign(num: float):
#https://www.youtube.com/watch?v=KOYoMYWUTEo return 1 if num >= 0 else -1
def determine_director_coefficient(segment: Segment):
return ((segment.start.y - segment.stop.y) / (segment.start.x - segment.stop.x))
def determine_ordinate_at_origin(point: Point, director_cofficient: float): def get_impact_point(segments: list[Segment], ball: Ball):
return point.y - point.x * director_cofficient
def determine_intersection(director_coefficient1: float, ordinate_at_origin1: float, director_coefficient2: float, ordinate_at_origin2: float): angle_radian: float = ball.angle * math.pi / 180
if (director_coefficient1 == director_coefficient2):
return None
return (ordinate_at_origin1 + ordinate_at_origin2) / (director_coefficient1 + director_coefficient2)
def determine_intersections(ball: Ball, segments: list[Segment]): direction_vector: Vector = Vector(math.cos(angle_radian), math.sin(angle_radian))
intersections: list[Point] = [] 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: for segment in segments:
# form m * x + p segment_vector: Vector = Vector(segment.start.x - segment.stop.x, segment.start.y - segment.stop.y)
m: float = determine_director_coefficient(segment) segment_vector_unit = segment_vector / segment_vector.norm
p: float = determine_ordinate_at_origin(segment.start, m)
x: float = determine_intersection(m, p, ball.velocity_y, 0) scalar: float = segment_vector_unit.scalar(direction_vector)
if (x is None): if (scalar < 0.01):
continue continue
y: float = m * x + p print(segment_vector, segment_vector_unit, direction_vector)
intersections.append(Point(x, y)) distance: float = scalar * segment_vector.norm / 2
return intersections impact_x: float = position.x + distance * direction_vector.x
def determine_distance_between_ball_and_wall(ball: Ball, segments: list[Segment]): impact_y: float = position.y + distance * direction_vector.y
intersections: list[Point] = determine_intersections(ball, segments) impact: Point = Point(impact_x, impact_y)
distances = list(map(math.dist, intersections)) print("impact", impact)
return min(distances) return impact
def render(ball: Ball, game: Game): async def update_ball(game: Game, impact: Point):
segments: list[Segment] = [player.rail for player in game.players] 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)
print(determine_distance_between_ball_and_wall(ball))
def routine(game: Game): def routine(game: Game):
asyncio.run(render(game))
while True: while True:
for player in game._updated_players: for player in game._updated_players:
game.broadcast("update_paddle", player.to_dict(), [player]) game.broadcast("update_paddle", player.to_dict(), [player])
game._updated_players.clear() 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) sleep(1 / config.SERVER_TPS)