Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: punch via spacebar #24

Merged
merged 4 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="web site for the hips application"
/>
<meta name="description" content="web site for the hips application" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
Expand Down
4 changes: 3 additions & 1 deletion server/socketServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ const socketConfig = process.env.NODE_ENV === 'prod'
};
const io = new socket_io_1.Server(socketConfig);
let playerCount = 0;
// the Map object holds key-value pairs and remembers the original insertion order of the keys
// NOTE: the Map object holds key-value pairs
// and remembers the original insertion order of the keys
const socketToPlayerId = new Map();
io.on('connection', (socket) => {
console.log('socket server connected to client');
playerCount += 1;
io.emit('playerCountUpdate', playerCount);
if (playerCount < 3) {
Expand Down
19 changes: 14 additions & 5 deletions server/socketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,34 @@ let playerCount = 0;
const socketToPlayerId = new Map<number, string>();

io.on('connection', (socket: SocketData) => {
console.log('socket server connected to client');
playerCount += 1;
io.emit('playerCountUpdate', playerCount);
if (playerCount < 3) {
socketToPlayerId.set(playerCount, socket.id);
// emit only back to sender to issue playerId
socket.emit('assignPlayerId', playerCount);

socket.on('updatePlayer1', (pos: Position) => {
socket.broadcast.emit('updatePlayer1', pos);
socket.on('p1Moving', (pos: Position) => {
// to all clients except sender
socket.broadcast.emit('p1Moving', pos);
});
socket.on('updatePlayer2', (pos: Position) => {
socket.broadcast.emit('updatePlayer2', pos);
socket.on('p2Moving', (pos: Position) => {
socket.broadcast.emit('p2Moving', pos);
});
socket.on('p1Punching', (isPunching: boolean, punchDirection: string) => {
// to all connected clients
io.emit('p1Punching', isPunching, punchDirection);
});
socket.on('p2Punching', (isPunching: boolean, punchDirection: string) => {
io.emit('p2Punching', isPunching, punchDirection);
});
}
socket.on('disconnect', () => {
playerCount -= 1;
socketToPlayerId.set(playerCount, socket.id);

// need to emit to the remaining player that they're now player1
// need to emit to the remaining player that they're now player1 when player1 disconnects
socket.broadcast.emit('assignPlayerId', playerCount);
io.emit('playerCountUpdate', playerCount);
});
Expand Down
12 changes: 6 additions & 6 deletions server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
Expand All @@ -25,7 +25,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */

/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
Expand Down Expand Up @@ -71,12 +71,12 @@
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
Expand All @@ -98,6 +98,6 @@

/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
6 changes: 6 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@
.player {
position: absolute;
}

.punch-line {
width: 24px;
height: 2px;
position: absolute;
}
63 changes: 57 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { io, Socket } from 'socket.io-client';
import { DefaultEventsMap } from '@socket.io/component-emitter';
import PunchLine from './PunchLine';
import './App.css';

let socket: Socket<DefaultEventsMap, DefaultEventsMap>;
Expand All @@ -11,6 +12,10 @@ function App() {
const [posY1, setPosY1] = useState(150);
const [posX2, setPosX2] = useState(50);
const [posY2, setPosY2] = useState(50);
const [p1Punching, setP1Punching] = useState(false);
const [p2Punching, setP2Punching] = useState(false);
const [p1PunchDirection, setP1PunchDirection] = useState('');
const [p2PunchDirection, setP2PunchDirection] = useState('');

// collision refs
const player1Ref = useRef<HTMLDivElement>(null);
Expand All @@ -20,6 +25,7 @@ function App() {
// other state
const [moving, setMoving] = useState(false);
const [direction, setDirection] = useState('');
const [lastDirection, setLastDirection] = useState('n');
const [playerCount, setPlayerCount] = useState(0);
const [playerId, setPlayerId] = useState(0);

Expand All @@ -34,6 +40,15 @@ function App() {
d: 'e',
};

const validPunchDirection = () => {
let dir = direction.slice(0, 2);
if (dir === 'ew' || dir === 'we' || dir === 'sn' || dir === 'ns') {
return direction[1];
} else {
return dir;
}
};

const isValidDirection = (key: string, dir: string) =>
((key === 'ArrowUp' || key === 'w') && !dir.includes('n')) ||
((key === 'ArrowDown' || key === 's') && !dir.includes('s')) ||
Expand All @@ -44,10 +59,22 @@ function App() {
if (isValidDirection(e.key, direction)) {
setMoving(true);
setDirection(direction + directionMap[e.key]);
} else if (e.key === ' ') {
if (e.repeat) return;
const punchDirection =
direction === '' ? lastDirection : validPunchDirection();
// emitting pattern where the socket controls the state:
// key press emits the event, the socket server emits back to all clients
socket.emit(`p${playerId}Punching`, true, punchDirection);
setTimeout(() => {
socket.emit(`p${playerId}Punching`, false, '');
}, 150);
}
};

const handleKeyUp = (e: KeyboardEvent) => {
if (e.key === ' ') return;
setLastDirection(direction);
const mappedDirection = directionMap[e.key];
if (mappedDirection) {
const i = direction.indexOf(mappedDirection);
Expand All @@ -67,10 +94,10 @@ function App() {
const borderHitBox = borderDiv!.getBoundingClientRect();

const checkCollision = (side: keyof DOMRect) =>
playerHitBox[side] <= +borderHitBox[side] + borderBuffer;
+playerHitBox[side] <= +borderHitBox[side] + borderBuffer;

const checkCollisionAlt = (side: keyof DOMRect) =>
playerHitBox[side] >= +borderHitBox[side] - borderBuffer;
+playerHitBox[side] >= +borderHitBox[side] - borderBuffer;

let borderDetected = new Set<string>();
if (checkCollision('left')) {
Expand Down Expand Up @@ -100,11 +127,11 @@ function App() {
if (playerId === 1) {
setPosX1(posX1 + dx);
setPosY1(posY1 + dy);
socket.emit('updatePlayer1', { x: posX1, y: posY1 });
socket.emit('p1Moving', { x: posX1, y: posY1 });
} else {
setPosX2(posX2 + dx);
setPosY2(posY2 + dy);
socket.emit('updatePlayer2', { x: posX2, y: posY2 });
socket.emit('p2Moving', { x: posX2, y: posY2 });
}
};

Expand Down Expand Up @@ -187,14 +214,22 @@ function App() {
socket.on('assignPlayerId', (id: number) => {
setPlayerId(id);
});
socket.on('updatePlayer1', (pos) => {
socket.on('p1Moving', (pos) => {
setPosX1(pos.x);
setPosY1(pos.y);
});
socket.on('updatePlayer2', (pos) => {
socket.on('p2Moving', (pos) => {
setPosX2(pos.x);
setPosY2(pos.y);
});
socket.on('p1Punching', (isPunching, punchDirection) => {
setP1Punching(isPunching);
setP1PunchDirection(punchDirection);
});
socket.on('p2Punching', (isPunching, punchDirection) => {
setP2Punching(isPunching);
setP2PunchDirection(punchDirection);
});
socket.on('display', (res: string) => {
console.log(res);
});
Expand Down Expand Up @@ -248,6 +283,22 @@ function App() {
}}
/>
)}
{p1Punching && (
<PunchLine
punchDirection={p1PunchDirection}
posX={posX1}
posY={posY1}
color='white'
/>
)}
{p2Punching && (
<PunchLine
punchDirection={p2PunchDirection}
posX={posX2}
posY={posY2}
color='red'
/>
)}
</div>
);
}
Expand Down
41 changes: 41 additions & 0 deletions src/PunchLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect } from 'react';

interface Props {
punchDirection: string;
posX: number;
posY: number;
color: string;
}

function PunchLine({ punchDirection, posX, posY, color }: Props) {
useEffect(() => {
console.log({ punchDirection });
}, [punchDirection]);

const punchDirectionMap = {
n: { top: posY - 10, left: posX },
e: { top: posY + 11, left: posX + 21, transform: 'rotate(90deg)' },
s: { top: posY + 32, left: posX },
w: { top: posY + 11, left: posX - 21, transform: 'rotate(90deg)' },
ne: { top: posY - 5, left: posX + 16, transform: 'rotate(45deg)' },
en: { top: posY - 5, left: posX + 16, transform: 'rotate(45deg)' },
se: { top: posY + 28, left: posX + 17, transform: 'rotate(135deg)' },
es: { top: posY + 28, left: posX + 17, transform: 'rotate(135deg)' },
sw: { top: posY + 28, left: posX - 16, transform: 'rotate(45deg)' },
ws: { top: posY + 28, left: posX - 16, transform: 'rotate(45deg)' },
nw: { top: posY - 5, left: posX - 16, transform: 'rotate(135deg)' },
wn: { top: posY - 5, left: posX - 16, transform: 'rotate(135deg)' },
};

return (
<div
className='punch-line'
style={{
...punchDirectionMap[punchDirection as keyof typeof punchDirectionMap],
backgroundColor: color,
}}
/>
);
}

export default PunchLine;