pong now in TypeScript
This commit is contained in:
33
src/pong.html
Normal file
33
src/pong.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
}
|
||||
#canvas-container {
|
||||
text-align: center;
|
||||
border: dashed red 5px;
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
/* width: 80%; */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="startGame()">
|
||||
|
||||
<div id="canvas-container">
|
||||
<!-- <p> =) </p> -->
|
||||
</div>
|
||||
|
||||
<!-- <script type="module" src="pong.js"></script> -->
|
||||
<script src="../build/pong.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
359
src/pong.ts
Normal file
359
src/pong.ts
Normal file
@@ -0,0 +1,359 @@
|
||||
|
||||
// import {component, score, line} from "./class.js";
|
||||
// @ts-check
|
||||
|
||||
let gridDisplay = false;
|
||||
|
||||
let pong: gameArea;
|
||||
|
||||
let wall_top: Rectangle;
|
||||
let wall_bottom: Rectangle;
|
||||
let player1: Player;
|
||||
let player2: Player;
|
||||
let ball: Ball;
|
||||
let score1: TextElem;
|
||||
let score2: TextElem;
|
||||
|
||||
let w_grid_mid: Rectangle;
|
||||
let w_grid_u1: Rectangle;
|
||||
let w_grid_d1: Rectangle;
|
||||
let h_grid_mid: Rectangle;
|
||||
let h_grid_u1: Rectangle;
|
||||
let h_grid_d1: Rectangle;
|
||||
|
||||
function startGame()
|
||||
{
|
||||
pong = new gameArea();
|
||||
|
||||
// Const
|
||||
let w = pong.canvas.width;
|
||||
let h = pong.canvas.height;
|
||||
let w_mid = w/2;
|
||||
let h_mid = h/2;
|
||||
const pw = w/50;
|
||||
const ph = pw*5;
|
||||
const ball_size = pw; // const ball_size = w/50;
|
||||
const score_size = w/16;
|
||||
const wall_size = w/100;
|
||||
const playerSpeed = w/75;
|
||||
const ballSpeed = w/75;
|
||||
|
||||
// Component
|
||||
wall_top = new Rectangle({x: 0, y: 0}, "grey", w, wall_size);
|
||||
wall_bottom = new Rectangle({x: 0, y: h-wall_size}, "grey", w, wall_size);
|
||||
|
||||
player1 = new Player({x: 0+w/50, y: h_mid-ph/2}, "white", pw, ph);
|
||||
player2 = new Player({x: w-w/50-pw, y: h_mid-ph/2}, "white", pw, ph);
|
||||
player1.speed = playerSpeed;
|
||||
player2.speed = playerSpeed;
|
||||
|
||||
ball = new Ball({x: w_mid-ball_size/2, y: h_mid-ball_size/2}, "white", ball_size);
|
||||
ball.speed = ballSpeed;
|
||||
ball.dir.x = -0.8;
|
||||
ball.dir.y = +0.2;
|
||||
|
||||
score1 = new TextElem({x: w_mid-w/8, y: w/12}, "white", score_size+"px");
|
||||
score1.text = "0";
|
||||
score2 = new TextElem({x: w_mid+w/8-score_size/2, y: w/12}, "white", score_size+"px");
|
||||
score2.text = "0";
|
||||
|
||||
// Grid
|
||||
const grid_size = w/500;
|
||||
w_grid_mid = new Rectangle({x: 0, y: h_mid}, "darkgreen", w, grid_size);
|
||||
w_grid_u1 = new Rectangle({x: 0, y: h/4}, "darkgreen", w, grid_size);
|
||||
w_grid_d1 = new Rectangle({x: 0, y: h-h/4}, "darkgreen", w, grid_size);
|
||||
h_grid_mid = new Rectangle({x: w_mid, y: 0}, "darkgreen", grid_size, h);
|
||||
h_grid_u1 = new Rectangle({x: w/4, y: 0}, "darkgreen", grid_size, h);
|
||||
h_grid_d1 = new Rectangle({x: w-w/4, y: 0}, "darkgreen", grid_size, h);
|
||||
|
||||
// dashed line TODO
|
||||
// midLine = new component(grid_size, h, "white", w_mid, 0);
|
||||
|
||||
pong.start();
|
||||
}
|
||||
|
||||
class gameArea {
|
||||
keys: string[];
|
||||
interval: number = 0;
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
constructor() {
|
||||
this.keys = [];
|
||||
// this.canvas = {};
|
||||
this.canvas = document.createElement("canvas");
|
||||
// this.ctx = {};
|
||||
this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
/* ratio 5/3 (1.66) */
|
||||
const ratio = 1.66666;
|
||||
this.canvas.width = 1500;
|
||||
this.canvas.height = this.canvas.width / ratio;
|
||||
let container = document.getElementById("canvas-container");
|
||||
if (container)
|
||||
container.insertBefore(this.canvas, container.childNodes[0]);
|
||||
}
|
||||
start() {
|
||||
this.interval = setInterval(gameLoop, 20);
|
||||
window.addEventListener('keydown', function (e) { pong.addKey(e.key); });
|
||||
window.addEventListener('keyup', function (e) { pong.deleteKey(e.key); });
|
||||
}
|
||||
addKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
var i = pong.keys.indexOf(key);
|
||||
if (i == -1)
|
||||
pong.keys.push(key);
|
||||
}
|
||||
deleteKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
var i = pong.keys.indexOf(key);
|
||||
if (i != -1)
|
||||
pong.keys.splice(i, 1);
|
||||
}
|
||||
clear() {
|
||||
// @ts-ignore
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
// Bad drawLine, TODO make a drawLine with fillRect()
|
||||
// function drawLine(start, end, color, pattern)
|
||||
// {
|
||||
// let ctx = pong.ctx;
|
||||
// ctx.beginPath();
|
||||
// ctx.setLineDash(pattern);
|
||||
// ctx.moveTo(start[0], start[1]);
|
||||
// ctx.lineTo(end[0], end[1]);
|
||||
// ctx.strokeStyle = color;
|
||||
// ctx.stroke();
|
||||
// }
|
||||
|
||||
function gameLoop()
|
||||
{
|
||||
handleInput();
|
||||
|
||||
ball.move();
|
||||
if (ball.collision(wall_top) || ball.collision(wall_bottom))
|
||||
ball.bounce();
|
||||
else if (ball.collision(player1))
|
||||
ball.bounce(player1);
|
||||
else if (ball.collision(player2))
|
||||
ball.bounce(player2);
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
function draw()
|
||||
{
|
||||
pong.clear();
|
||||
|
||||
if (gridDisplay)
|
||||
drawGrid();
|
||||
|
||||
// drawLine([w_mid, 0], [w_mid, h], "white", [10, 10]); // bad
|
||||
|
||||
wall_top.update();
|
||||
wall_bottom.update();
|
||||
player1.update();
|
||||
player2.update();
|
||||
ball.update();
|
||||
score1.update();
|
||||
score2.update();
|
||||
}
|
||||
|
||||
function drawGrid()
|
||||
{
|
||||
w_grid_mid.update();
|
||||
w_grid_u1.update();
|
||||
w_grid_d1.update();
|
||||
|
||||
h_grid_mid.update();
|
||||
h_grid_u1.update();
|
||||
h_grid_d1.update();
|
||||
}
|
||||
|
||||
function handleInput()
|
||||
{
|
||||
var keys = pong.keys;
|
||||
if (keys.length == 0)
|
||||
return;
|
||||
|
||||
if (keys.indexOf("g") != -1)
|
||||
{
|
||||
gridDisplay = !gridDisplay;
|
||||
pong.deleteKey("g");
|
||||
}
|
||||
playerMove(keys);
|
||||
}
|
||||
|
||||
function playerMove(keys: string[])
|
||||
{
|
||||
player1.dir.y = 0;
|
||||
if (keys.indexOf("w") != -1)
|
||||
{ player1.dir.y += -1; }
|
||||
if (keys.indexOf("s") != -1)
|
||||
{ player1.dir.y += 1; }
|
||||
player1.move();
|
||||
if (player1.collision(wall_top) || player1.collision(wall_bottom))
|
||||
{
|
||||
player1.dir.y = player1.dir.y * -1;
|
||||
player1.move();
|
||||
}
|
||||
|
||||
player2.dir.y = 0;
|
||||
if (keys.indexOf("ArrowUp".toLowerCase()) != -1)
|
||||
{ player2.dir.y += -1; }
|
||||
if (keys.indexOf("ArrowDown".toLowerCase()) != -1)
|
||||
{ player2.dir.y += 1; }
|
||||
player2.move();
|
||||
if (player2.collision(wall_top) || player2.collision(wall_bottom))
|
||||
{
|
||||
player2.dir.y = player2.dir.y * -1;
|
||||
player2.move();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// class.js
|
||||
|
||||
type Vector = {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
|
||||
interface Component {
|
||||
pos: Vector;
|
||||
color: string;
|
||||
// ctx: CanvasRenderingContext2D;
|
||||
update(): void;
|
||||
}
|
||||
|
||||
|
||||
class Rectangle implements Component {
|
||||
pos: Vector;
|
||||
color: string;
|
||||
width: number;
|
||||
height: number;
|
||||
constructor(pos: Vector, color: string, width: number, height: number) {
|
||||
this.pos = pos;
|
||||
this.color = color;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
update() {
|
||||
let ctx = pong.ctx;
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
}
|
||||
collision(collider: Rectangle): boolean { // Collision WIP. To redo
|
||||
var myleft = this.pos.x;
|
||||
var myright = this.pos.x + (this.width);
|
||||
var mytop = this.pos.y;
|
||||
var mybottom = this.pos.y + (this.height);
|
||||
var otherleft = collider.pos.x;
|
||||
var otherright = collider.pos.x + (collider.width);
|
||||
var othertop = collider.pos.y;
|
||||
var otherbottom = collider.pos.y + (collider.height);
|
||||
if ((mybottom < othertop)
|
||||
|| (mytop > otherbottom)
|
||||
|| (myright < otherleft)
|
||||
|| (myleft > otherright)) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Moving {
|
||||
dir: Vector;
|
||||
speed: number;
|
||||
move(): void;
|
||||
}
|
||||
|
||||
|
||||
class Player extends Rectangle implements Moving {
|
||||
dir: Vector = {x: 0.0, y: 0.0};
|
||||
speed: number = 1;
|
||||
constructor(pos: Vector, color: string, width: number, height: number) {
|
||||
super(pos, color, width, height);
|
||||
}
|
||||
move() {
|
||||
this.pos.x += this.dir.x * this.speed;
|
||||
this.pos.y += this.dir.y * this.speed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ball extends Rectangle implements Moving {
|
||||
dir: Vector = {x: 0.0, y: 0.0};
|
||||
speed: number = 1;
|
||||
constructor(pos: Vector, color: string, size: number) {
|
||||
super(pos, color, size, size);
|
||||
}
|
||||
move() {
|
||||
this.pos.x += this.dir.x * this.speed;
|
||||
this.pos.y += this.dir.y * this.speed;
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
if (collider instanceof Player)
|
||||
this._bouncePlayer(collider);
|
||||
else // Could be more generic, but it's OK, because in Pong collider can only be Player or Wall.
|
||||
this._bounceWall();
|
||||
|
||||
}
|
||||
private _bounceWall() { // Should be enough for Wall
|
||||
ball.dir.y = ball.dir.y * -1;
|
||||
}
|
||||
private _bouncePlayer(collider: Player) { // WIP
|
||||
// Bounce for Player need to be more complexe than this
|
||||
ball.dir.x = ball.dir.x * -1;
|
||||
}
|
||||
}
|
||||
|
||||
// conflict with Text
|
||||
class TextElem implements Component {
|
||||
pos: Vector;
|
||||
color: string;
|
||||
size: string;
|
||||
font: string = "Consolas";
|
||||
text: string = "";
|
||||
constructor(pos: Vector, color: string, size: string, font?: string) {
|
||||
this.pos = pos;
|
||||
this.color = color;
|
||||
this.size = size;
|
||||
if (font)
|
||||
this.font = font;
|
||||
}
|
||||
update() {
|
||||
let ctx = pong.ctx;
|
||||
ctx.font = this.size + " " + this.font;
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fillText(this.text, this.pos.x, this.pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
// class line {
|
||||
// width: number;
|
||||
// height: number;
|
||||
// color: string;
|
||||
// x: number;
|
||||
// y: number;
|
||||
// constructor(width: number, height: number, color: string, x: number, y: number) {
|
||||
// this.width = width;
|
||||
// this.height = height;
|
||||
// this.color = color;
|
||||
// this.x = x;
|
||||
// this.y = y;
|
||||
// }
|
||||
// update() {
|
||||
// let ctx = pong.ctx;
|
||||
// ctx.fillStyle = this.color;
|
||||
// ctx.fillRect(this.x, this.y, this.width, this.height);
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user