Added crude client rendering with pixi.js
Added keyboard controlling Added fixed ball reflections on right paddle added reflections on screen borders Added goal (miss the paddle) detection
This commit is contained in:
@ -9,20 +9,22 @@ namespace PongGame.Hubs;
|
||||
public struct PongGameState {
|
||||
public const float HEIGHT = 500.0f;
|
||||
public const float WIDTH = 2 * HEIGHT;
|
||||
public const float PADDLE1_OFFSET = 50.0f;
|
||||
public const float PADDLE2_OFFSET = 1000 - PADDLE1_OFFSET;
|
||||
public const float PADDLE1_OFFSET = WIDTH / 20;
|
||||
public const float PADDLE2_OFFSET = WIDTH - PADDLE1_OFFSET;
|
||||
|
||||
public static readonly PongGameState Initial = new() {
|
||||
BallState = PongBallState.Initial,
|
||||
Paddle1 = PongPaddleState.Initial,
|
||||
Paddle2 = PongPaddleState.Initial,
|
||||
Status = GameStatus.WaitingForPlayers
|
||||
Status = GameStatus.WaitingForPlayers,
|
||||
WinnerLeft = null
|
||||
};
|
||||
|
||||
public PongPaddleState Paddle1;
|
||||
public PongPaddleState Paddle2;
|
||||
public PongBallState BallState;
|
||||
public GameStatus Status;
|
||||
public bool? WinnerLeft;
|
||||
|
||||
public static void Update(ref PongGameState state) {
|
||||
if (state.Status is not GameStatus.InProgress) return;
|
||||
@ -30,6 +32,12 @@ public struct PongGameState {
|
||||
PongPaddleState.Update(ref state.Paddle1);
|
||||
PongPaddleState.Update(ref state.Paddle2);
|
||||
PongBallState.Update(ref state.BallState);
|
||||
var ballX = state.BallState.Pos.X;
|
||||
if (ballX is < 0 or > WIDTH) {
|
||||
state.WinnerLeft = ballX < 0;
|
||||
state.Status = GameStatus.Finished;
|
||||
return;
|
||||
}
|
||||
|
||||
Collide(in state.Paddle1, ref state.BallState, true);
|
||||
Collide(in state.Paddle2, ref state.BallState, false);
|
||||
@ -41,40 +49,48 @@ public struct PongGameState {
|
||||
if (intersection.IsEmpty) return;
|
||||
|
||||
// TODO: continuous collision
|
||||
var ratio = (ballState.Y - paddle.Height + PongPaddleState.PADDLE_HALF_LENGTH) / PongPaddleState.PADDLE_LENGTH;
|
||||
var upAngle = left ? MathF.PI * 3 / 8 : MathF.PI * 5 / 8;
|
||||
var downAngle = -upAngle;
|
||||
var ratio = (ballState.Pos.Y - paddle.Height + PongPaddleState.PADDLE_HALF_LENGTH) / PongPaddleState.PADDLE_LENGTH;
|
||||
|
||||
// TODO: lesser angles
|
||||
var upAngle = left ? MathF.PI * 3 / 8 : MathF.PI * 5 / 8;
|
||||
var downAngle = left ? -MathF.PI * 3 / 8 : MathF.PI * 11 / 8;
|
||||
|
||||
// TODO: reflect ball on surface instead of launching
|
||||
ballState.BallAngle = ratio * downAngle + (1 - ratio) * upAngle;
|
||||
}
|
||||
|
||||
public struct PongBallState {
|
||||
public const float BALL_SPEED = 8;
|
||||
public const float BALL_RADIUS = 2;
|
||||
public const float BALL_SPEED = 2 * BALL_RADIUS;
|
||||
public const float BALL_RADIUS = HEIGHT / 125;
|
||||
|
||||
public static readonly PongBallState Initial = new() {
|
||||
BallAngle = 0.0f,
|
||||
X = WIDTH / 2,
|
||||
Y = HEIGHT / 2
|
||||
Pos = new() {
|
||||
X = WIDTH / 2,
|
||||
Y = HEIGHT / 2
|
||||
}
|
||||
};
|
||||
|
||||
public float X;
|
||||
public float Y;
|
||||
public PointF Pos;
|
||||
public float BallAngle;
|
||||
|
||||
public static void Update(ref PongBallState state) {
|
||||
var (dy, dx) = MathF.SinCos(state.BallAngle);
|
||||
state.X += BALL_SPEED * dx;
|
||||
state.Y -= BALL_SPEED * dy;
|
||||
state.Pos.X += BALL_SPEED * dx;
|
||||
state.Pos.Y -= BALL_SPEED * dy;
|
||||
|
||||
if (state.Pos.Y < BALL_RADIUS
|
||||
|| state.Pos.Y > HEIGHT - BALL_RADIUS)
|
||||
state.BallAngle = 2 * MathF.PI - state.BallAngle;
|
||||
}
|
||||
|
||||
public RectangleF GetCollider() => new(X - BALL_RADIUS, Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
|
||||
public RectangleF GetCollider() => new(Pos.X - BALL_RADIUS, Pos.Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
|
||||
}
|
||||
|
||||
public struct PongPaddleState {
|
||||
public const float PADDLE_LENGTH = HEIGHT / 10;
|
||||
public const float PADDLE_HALF_LENGTH = PADDLE_LENGTH / 2;
|
||||
public const float PADDLE_WIDTH = PADDLE_LENGTH / 6;
|
||||
public const float PADDLE_WIDTH = PADDLE_LENGTH / 5;
|
||||
public const float PADDLE_SPEED = 8;
|
||||
|
||||
public static readonly PongPaddleState Initial = new() {
|
||||
@ -86,9 +102,9 @@ public struct PongGameState {
|
||||
public PongPaddleDirection Direction;
|
||||
|
||||
public static void Update(ref PongPaddleState state) {
|
||||
state.Height = Math.Clamp(state.Height + ((int)state.Direction) * PADDLE_SPEED, PADDLE_HALF_LENGTH, HEIGHT - PADDLE_HALF_LENGTH);
|
||||
state.Height = Math.Clamp(state.Height - ((int)state.Direction) * PADDLE_SPEED, PADDLE_HALF_LENGTH, HEIGHT - PADDLE_HALF_LENGTH);
|
||||
}
|
||||
|
||||
public RectangleF GetCollider(float x) => new(x - PADDLE_HALF_LENGTH, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH);
|
||||
public RectangleF GetCollider(float x) => new(x - PADDLE_WIDTH / 2, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user