Compare commits
7 Commits
f728240db9
...
main
Author | SHA1 | Date | |
---|---|---|---|
72275c491a
|
|||
64fe3d69dd
|
|||
9c328059fe
|
|||
531c0e1344
|
|||
391b5569a4
|
|||
2fb395a8d4
|
|||
cd7a1f15a9
|
@ -1,4 +1,6 @@
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using MessagePack;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
@ -16,15 +18,13 @@ public struct PongGameState {
|
||||
BallState = PongBallState.Initial,
|
||||
Paddle1 = PongPaddleState.Initial,
|
||||
Paddle2 = PongPaddleState.Initial,
|
||||
Status = GameStatus.WaitingForPlayers,
|
||||
WinnerLeft = null
|
||||
Status = GameStatus.WaitingForPlayers
|
||||
};
|
||||
|
||||
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;
|
||||
@ -32,15 +32,24 @@ 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);
|
||||
|
||||
switch (state.BallState.Pos.X) {
|
||||
case > WIDTH:
|
||||
AnnounceScore(ref state, ref state.Paddle1);
|
||||
return;
|
||||
case < 0:
|
||||
AnnounceScore(ref state, ref state.Paddle2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AnnounceScore(ref PongGameState state, ref PongPaddleState winner) {
|
||||
winner.Score++;
|
||||
state.Paddle1.ResetPosition();
|
||||
state.Paddle2.ResetPosition();
|
||||
state.BallState.ResetPosition();
|
||||
}
|
||||
|
||||
private static void Collide(in PongPaddleState paddle, ref PongBallState ballState, bool left) {
|
||||
@ -79,32 +88,61 @@ public struct PongGameState {
|
||||
state.Pos.X += BALL_SPEED * dx;
|
||||
state.Pos.Y -= BALL_SPEED * dy;
|
||||
|
||||
if (state.Pos.Y < BALL_RADIUS
|
||||
|| state.Pos.Y > HEIGHT - BALL_RADIUS)
|
||||
// If reflected from lower or upper border calculate continuous reflection
|
||||
// This prevents the ball from temporarily glitching into paddles
|
||||
if (state.Pos.Y < BALL_RADIUS) {
|
||||
state.Pos.Y = 2 * BALL_RADIUS - state.Pos.Y;
|
||||
state.BallAngle = 2 * MathF.PI - state.BallAngle;
|
||||
} else if (state.Pos.Y > HEIGHT - BALL_RADIUS) {
|
||||
state.Pos.Y = 2 * (HEIGHT - BALL_RADIUS) - state.Pos.Y;
|
||||
state.BallAngle = 2 * MathF.PI - state.BallAngle;
|
||||
}
|
||||
}
|
||||
|
||||
public RectangleF GetCollider() => new(Pos.X - BALL_RADIUS, Pos.Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
|
||||
|
||||
public void ResetPosition() {
|
||||
BallAngle = 0.0f;
|
||||
Pos.X = WIDTH / 2;
|
||||
Pos.Y = HEIGHT / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PongPaddleState {
|
||||
public const float PADDLE_LENGTH = HEIGHT / 10;
|
||||
public const float PADDLE_LENGTH = HEIGHT / 6;
|
||||
public const float PADDLE_HALF_LENGTH = PADDLE_LENGTH / 2;
|
||||
public const float PADDLE_WIDTH = PADDLE_LENGTH / 5;
|
||||
public const float PADDLE_SPEED = 8;
|
||||
private const float PADDLE_INITIAL_HEIGHT = HEIGHT / 2;
|
||||
|
||||
public static readonly PongPaddleState Initial = new() {
|
||||
Score = 0,
|
||||
Direction = PongPaddleDirection.Stop,
|
||||
Height = HEIGHT / 2
|
||||
Height = PADDLE_INITIAL_HEIGHT
|
||||
};
|
||||
|
||||
public float Height;
|
||||
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);
|
||||
[IgnoreMember] private int _score;
|
||||
public int Score {
|
||||
get => _score; set {
|
||||
if (_score != value) {
|
||||
_score = value;
|
||||
_ = Task.Run(() => ScoreChanged.Invoke(this, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<int> ScoreChanged;
|
||||
|
||||
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);
|
||||
|
||||
public RectangleF GetCollider(float x) => new(x - PADDLE_WIDTH / 2, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH);
|
||||
|
||||
public void ResetPosition() {
|
||||
Direction = PongPaddleDirection.Stop;
|
||||
Height = PADDLE_INITIAL_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ public class PongHub : Hub<IPongClient> {
|
||||
public override Task OnConnectedAsync() {
|
||||
Player = Lobby.CreatePlayer();
|
||||
Player.Client = Clients.Client(Context.ConnectionId);
|
||||
Player.Username = "Anon";
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -42,10 +41,10 @@ public class PongHub : Hub<IPongClient> {
|
||||
return Task.FromResult(room.ID);
|
||||
}
|
||||
|
||||
public Task<string> JoinRoom(string roomId) {
|
||||
public async Task<string> JoinRoom(string roomId) {
|
||||
AssertNotInRoom();
|
||||
var room = Lobby.JoinRoom(Player, roomId);
|
||||
return Task.FromResult(room.ID);
|
||||
var room = await Lobby.JoinRoom(Player, roomId);
|
||||
return room.ID;
|
||||
}
|
||||
|
||||
public Task MovePaddle(int dir) {
|
||||
@ -53,21 +52,14 @@ public class PongHub : Hub<IPongClient> {
|
||||
var direction = (PongPaddleDirection)dir;
|
||||
if (!Enum.IsDefined(direction))
|
||||
throw new HubException($"Invalid direction: {dir}!");
|
||||
room.MovePaddle(Player, direction);
|
||||
return Task.CompletedTask;
|
||||
return room.MovePaddle(Player, direction);
|
||||
}
|
||||
|
||||
public Task LeaveRoom() {
|
||||
Lobby.LeaveRoom(Player);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public Task LeaveRoom()
|
||||
=> Lobby.LeaveRoom(Player);
|
||||
|
||||
public Task RequestUsernameChange(string username) {
|
||||
// TOOD: check this
|
||||
Logger.LogInformation($"Player {Player} requested username change to [{username}]");
|
||||
Player.Username = username;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public Task RequestUsernameChange(string username)
|
||||
=> Lobby.ChangeUsername(Player, username);
|
||||
|
||||
public override Task OnDisconnectedAsync(Exception? exception) {
|
||||
Lobby.RemovePlayer(Player);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
@ -36,19 +37,24 @@ public class PongLobby {
|
||||
return room;
|
||||
}
|
||||
|
||||
public PongRoom JoinRoom(PongPlayer player, string roomId) {
|
||||
public Task<PongRoom> JoinRoom(PongPlayer player, string roomId) {
|
||||
PongRoom? room;
|
||||
lock (PongRooms) {
|
||||
room = PongRooms.GetValueOrDefault(roomId);
|
||||
}
|
||||
if (room is null) throw new HubException($"Room [{roomId}] not found!");
|
||||
room.Join(player);
|
||||
return room;
|
||||
return Task.FromResult(room);
|
||||
}
|
||||
|
||||
public void LeaveRoom(PongPlayer player) {
|
||||
if (player.ConnectedRoom is PongRoom room)
|
||||
public Task LeaveRoom(PongPlayer player) {
|
||||
if (player.ConnectedRoom is PongRoom room) {
|
||||
room.Leave(player);
|
||||
if (room.IsEmpty)
|
||||
lock (PongRooms)
|
||||
_ = PongRooms.Remove(room.ID);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private readonly Random random = new();
|
||||
@ -62,4 +68,28 @@ public class PongLobby {
|
||||
} while (PongRooms.ContainsKey(id));
|
||||
return id;
|
||||
}
|
||||
|
||||
public async Task ChangeUsername(PongPlayer player, string username) {
|
||||
username = ValidateUsername(username);
|
||||
if (player.Username == username) {
|
||||
await player.Client.UsernameChanged(username);
|
||||
return;
|
||||
}
|
||||
lock (connectedPlayers) {
|
||||
// TODO: separate hashset for usernames
|
||||
if (connectedPlayers.Select(i => i.Username).Contains(username))
|
||||
throw new HubException($"Username {username} is already taken!");
|
||||
}
|
||||
Logger.LogInformation("Player {Player} requested username change to [{username}]", player, username);
|
||||
player.Username = username;
|
||||
}
|
||||
|
||||
private static readonly Regex UsernameRegex = new(@"^(?!.*[._ -]{2})[\w._ -]{3,20}$", RegexOptions.Compiled, TimeSpan.FromMilliseconds(200));
|
||||
|
||||
private static string ValidateUsername(string username) {
|
||||
username = username.Trim();
|
||||
if (!UsernameRegex.IsMatch(username))
|
||||
throw new HubException($"At most 20 characters, no two consecutive symbols");
|
||||
return username;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace PongGame.Hubs;
|
||||
|
||||
[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")]
|
||||
public class PongPlayer {
|
||||
private string username = default!;
|
||||
private string username = "UNNAMED";
|
||||
|
||||
public PongRoom? ConnectedRoom { get; internal set; }
|
||||
public string Username {
|
||||
|
@ -18,6 +18,8 @@ public class PongRoom {
|
||||
|
||||
public PongPlayer? Player1 { get; private set; }
|
||||
public PongPlayer? Player2 { get; private set; }
|
||||
public bool IsEmpty => Player1 is null && Player2 is null;
|
||||
|
||||
private PongGameState State = PongGameState.Initial;
|
||||
|
||||
public void Join(PongPlayer player) {
|
||||
@ -46,7 +48,7 @@ public class PongRoom {
|
||||
_ = Task.Run(PlayersChanged);
|
||||
}
|
||||
|
||||
private BackgroundWorker gameWorker = new() {
|
||||
private readonly BackgroundWorker gameWorker = new() {
|
||||
WorkerSupportsCancellation = true
|
||||
};
|
||||
|
||||
@ -67,7 +69,7 @@ public class PongRoom {
|
||||
if (Player1 is PongPlayer player1 && Player2 is PongPlayer player2) {
|
||||
Logger.LogInformation("[{ID}] Pong game started: {player1} vs. {player2}", ID, player1, player2);
|
||||
ResumeGame();
|
||||
} else if (Player1 is null && Player2 is null) {
|
||||
} else if (IsEmpty) {
|
||||
CloseRoom();
|
||||
} else
|
||||
PauseGame();
|
||||
@ -84,16 +86,15 @@ public class PongRoom {
|
||||
|
||||
public override string ToString() => $"[{ID}]";
|
||||
|
||||
public void MovePaddle(PongPlayer player, PongPaddleDirection direction) {
|
||||
public Task MovePaddle(PongPlayer player, PongPaddleDirection direction) {
|
||||
if (Player1 == player) {
|
||||
State.Paddle1.Direction = direction;
|
||||
Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction);
|
||||
return;
|
||||
Logger.LogDebug(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction);
|
||||
} else if (Player2 == player) {
|
||||
State.Paddle2.Direction = direction;
|
||||
Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 2, player, direction);
|
||||
return;
|
||||
}
|
||||
Logger.LogDebug(DIRECTION_LOG_TEMPLATE, ID, 2, player, direction);
|
||||
} else
|
||||
throw new InvalidOperationException("Player is not in this room, but moved! Assumably players room wasn't deleted.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
@ -8,20 +8,24 @@
|
||||
<h1 class="display-4">Pong</h1>
|
||||
<h3 id="connection">Connection Status</h3>
|
||||
|
||||
<button id="createlobby" class="btn btn-primary mb-3">Create</button>
|
||||
<button id="leavelobby" class="btn btn-primary mb-3">Leave</button>
|
||||
<button id="createlobby" class="btn btn-primary mb-3">Create Room</button>
|
||||
<button id="leavelobby" class="btn btn-primary mb-3 d-none">Leave Room</button>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div id="joinroomdiv" class="input-group mb-3">
|
||||
<input id="roomid" type="text" class="form-control" placeholder="Room ID" aria-label="Room ID" aria-describedby="joinroom">
|
||||
<button id="joinroom" class="btn btn-outline-secondary" type="button">Button</button>
|
||||
<button id="joinroom" class="btn btn-outline-secondary" type="button">Join Room</button>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group mb-3 has-validation">
|
||||
<input id="username" type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="setusername">
|
||||
<button id="setusername" class="btn btn-outline-secondary" type="button">Set Username</button>
|
||||
<div id="usernameerror" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Room <span id="connectedroomid"></span></h4>
|
||||
<div id="canvas-container" class="mb-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PongGame.Pages;
|
||||
public class PongModel : PageModel {
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PongGame.Pages;
|
||||
public class PrivacyModel : PageModel {
|
||||
|
@ -1,4 +1,3 @@
|
||||
using MessagePack;
|
||||
using PongGame.Hubs;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
53
PongGame/build.py
Normal file
53
PongGame/build.py
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
from argparse import ArgumentParser
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@dataclass
|
||||
class Version:
|
||||
major: int = 1
|
||||
minor: int = 0
|
||||
patch: int = 0
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.major}.{self.minor}.{self.patch}"
|
||||
|
||||
def __init__(self, version_str: str):
|
||||
try:
|
||||
match list(map(int, version_str.split("."))):
|
||||
case [major, minor, patch]:
|
||||
self.major, self.minor, self.patch = major, minor, patch
|
||||
return
|
||||
case _:
|
||||
raise ValueError("Version file must contain 3 parts (SemVer)!")
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Version must contain numbers only!") from e
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser("build.py", description="Docker multi-arch build helper.")
|
||||
version_group = parser.add_mutually_exclusive_group()
|
||||
version_group.add_argument("--major", "-m", action="store_true", help="Increase the major version before building.")
|
||||
version_group.add_argument("--minor", "-n", action="store_true", help="Increase the minor version before building.")
|
||||
version_group.add_argument("--patch", "-p", action="store_true", help="Increase the patch version before building.")
|
||||
parser.add_argument("--file", "-f", type=str, default="version.txt", help="File to store the current version in.")
|
||||
return parser.parse_args()
|
||||
|
||||
def save_version(version_file: Path, version: Version):
|
||||
with version_file.open("w", encoding="utf-8") as f:
|
||||
f.write(str(version))
|
||||
|
||||
def load_version(version_file: Path):
|
||||
try:
|
||||
with version_file.open("r", encoding="utf-8") as f:
|
||||
return Version(f.read().strip())
|
||||
except FileNotFoundError:
|
||||
return Version()
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
version_file = Path(args.file)
|
||||
version = load_version(version_file)
|
||||
save_version(version_file, version)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1
PongGame/version.txt
Normal file
1
PongGame/version.txt
Normal file
@ -0,0 +1 @@
|
||||
1.0.0
|
@ -11,26 +11,29 @@ function getElement(id) {
|
||||
}
|
||||
|
||||
const connectionStatus = getElement("connection");
|
||||
const createlobby = getElement("createlobby");
|
||||
const createroom = getElement("createlobby");
|
||||
const roomidinput = getElement("roomid");
|
||||
const joinroom = getElement("joinroom");
|
||||
const joinroomdiv = getElement("joinroomdiv");
|
||||
const usernameinput = getElement("username");
|
||||
const setusername = getElement("setusername");
|
||||
const leavelobby = getElement("leavelobby");
|
||||
const leaveroom = getElement("leavelobby");
|
||||
const usernameerror = getElement("usernameerror");
|
||||
const connectedroomid = getElement("connectedroomid");
|
||||
|
||||
connection.onclose(function (error) {
|
||||
if (error) {
|
||||
connection.onclose(function (err) {
|
||||
if (err) {
|
||||
connectionStatus.textContent = "Unexpected error!";
|
||||
return console.error(`Connection aborted: ${error.message}`);
|
||||
return console.error(`Connection aborted: ${err.message}`);
|
||||
}
|
||||
console.info("Disconnected!");
|
||||
connectionStatus.textContent = "Closed!";
|
||||
});
|
||||
|
||||
connection.onreconnecting(function (error) {
|
||||
if (error) {
|
||||
connection.onreconnecting(function (err) {
|
||||
if (err) {
|
||||
connectionStatus.textContent = "Reconnecting!";
|
||||
return console.error(`Connection reconnecting: ${error.message}`);
|
||||
return console.error(`Connection reconnecting: ${err.message}`);
|
||||
}
|
||||
console.info("Reconnecting!");
|
||||
connectionStatus.textContent = "Reconnecting!";
|
||||
@ -41,11 +44,20 @@ connection.onreconnected(function (connectionId) {
|
||||
connectionStatus.textContent = "Connected!";
|
||||
});
|
||||
|
||||
createlobby.addEventListener("click", function (event) {
|
||||
connection.invoke("CreateRoom").then(function (roomId) {
|
||||
function show(elem) { elem.classList.remove("d-none"); }
|
||||
function hide(elem) { elem.classList.add("d-none"); }
|
||||
|
||||
function roomJoined(roomId) {
|
||||
roomidinput.value = roomId;
|
||||
connectedroomid.textContent = roomId;
|
||||
console.info(`Joined room [${roomId}]`);
|
||||
}).catch(function (err) {
|
||||
hide(createroom);
|
||||
hide(joinroomdiv);
|
||||
show(leaveroom);
|
||||
}
|
||||
|
||||
createroom.addEventListener("click", function (event) {
|
||||
connection.invoke("CreateRoom").then(roomJoined).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
@ -57,24 +69,44 @@ connection.on("GameStateChanged", function (state) {
|
||||
connection.on("UsernameChanged", function (username) {
|
||||
console.info(`Username is now ${username}`);
|
||||
usernameinput.value = username;
|
||||
usernameinput.classList.remove("is-invalid");
|
||||
usernameinput.classList.add("is-valid");
|
||||
});
|
||||
|
||||
joinroom.addEventListener("click", function (event) {
|
||||
connection.invoke("JoinRoom", roomidinput.value).catch(function (err) {
|
||||
connection.invoke("JoinRoom", roomidinput.value).then(roomJoined).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
function hubExceptionMessage(msg) {
|
||||
const needle = "HubException: ";
|
||||
let idx = msg.lastIndexOf(needle);
|
||||
if (idx < 0) return msg;
|
||||
return msg.substr(idx + needle.length);
|
||||
}
|
||||
|
||||
usernameinput.addEventListener("input", function (event) {
|
||||
usernameinput.classList.remove("is-valid");
|
||||
});
|
||||
|
||||
setusername.addEventListener("click", function (event) {
|
||||
connection.invoke("RequestUsernameChange", usernameinput.value).catch(function (err) {
|
||||
usernameerror.textContent = hubExceptionMessage(err.message);
|
||||
usernameinput.classList.add("is-invalid");
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
leavelobby.addEventListener("click", function (event) {
|
||||
connection.invoke("LeaveRoom").catch(function (err) {
|
||||
leaveroom.addEventListener("click", function (event) {
|
||||
connection.invoke("LeaveRoom").then(function () {
|
||||
roomidinput.value = "";
|
||||
show(createroom);
|
||||
show(joinroomdiv);
|
||||
hide(leaveroom);
|
||||
}).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
@ -86,13 +118,9 @@ function movePaddle(direction) {
|
||||
});
|
||||
}
|
||||
|
||||
function moveUp() { return movePaddle(-1); }
|
||||
function stopPaddle() { return movePaddle(0); }
|
||||
function moveDown() { return movePaddle(1); }
|
||||
|
||||
// Create the application helper and add its render target to the page
|
||||
let app = new PIXI.Application({ width: 1000, height: 500 });
|
||||
getElement('canvas-container').appendChild(app.view);
|
||||
getElement("canvas-container").appendChild(app.view);
|
||||
|
||||
let graphics = new PIXI.Graphics();
|
||||
app.stage.addChild(graphics);
|
||||
@ -124,18 +152,25 @@ connection.on("ReceiveGameState", function (state) {
|
||||
const keyEvent = (function () {
|
||||
var upPressed = false;
|
||||
var downPressed = false;
|
||||
var direction = 0;
|
||||
|
||||
function setDirection(dir) {
|
||||
if (direction != dir) {
|
||||
direction = dir;
|
||||
movePaddle(direction);
|
||||
}
|
||||
}
|
||||
|
||||
function moveUpdated() {
|
||||
if (upPressed == downPressed) stopPaddle();
|
||||
else if (upPressed) moveUp();
|
||||
else if (downPressed) moveDown();
|
||||
if (upPressed == downPressed) setDirection(0);
|
||||
else if (upPressed) setDirection(-1);
|
||||
else if (downPressed) setDirection(1);
|
||||
else console.error("unknown move!");
|
||||
}
|
||||
|
||||
function handler(event) {
|
||||
if (event.repeat) return;
|
||||
if (event.path.indexOf(document.body) != 0) return; // only use key if it was pressed on empty space
|
||||
var pressed = event.type == "keyup";
|
||||
var pressed = event.type == "keydown";
|
||||
|
||||
// W Key is 87, Up arrow is 87
|
||||
// S Key is 83, Down arrow is 40
|
||||
switch (event.keyCode) {
|
||||
@ -150,6 +185,9 @@ const keyEvent = (function () {
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (event.repeat) return;
|
||||
if (event.target.tagName == 'INPUT') return; // dont use key if on input
|
||||
|
||||
event.preventDefault();
|
||||
moveUpdated();
|
||||
}
|
||||
@ -157,8 +195,8 @@ const keyEvent = (function () {
|
||||
return handler;
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', keyEvent);
|
||||
document.addEventListener('keyup', keyEvent);
|
||||
document.addEventListener("keydown", keyEvent);
|
||||
document.addEventListener("keyup", keyEvent);
|
||||
|
||||
connection.start().then(function () {
|
||||
console.info(`Connected!`);
|
||||
|
Reference in New Issue
Block a user