Skip to content

Commit

Permalink
feat: punch via spacebar (#24)
Browse files Browse the repository at this point in the history
* feat: n,e,s,w punch via spacebar

* dist

* diagonals

* connect punch to socket
  • Loading branch information
jdshaeffer committed Jan 14, 2024
1 parent 338d0e7 commit 1949760
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 22 deletions.
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;

0 comments on commit 1949760

Please sign in to comment.