Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from pixselve-school:refactor-class
Browse files Browse the repository at this point in the history
Refactor-class
  • Loading branch information
CodyAdam authored Jan 24, 2024
2 parents 89595d4 + bdf44f3 commit 646d30f
Show file tree
Hide file tree
Showing 17 changed files with 618 additions and 269 deletions.
226 changes: 79 additions & 147 deletions packages/client/src/app/canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,165 +1,97 @@
"use client";

import { useApi } from "@/hooks/useApi";
import { useMouse } from "@/hooks/useMouse";
import { useScreen } from "@/hooks/useScreen";
import { worldToScreen } from "@/utils/position";
import { TPS } from "@viper-vortex/shared";
import { throttle } from "lodash";
import { useEffect, useRef, useState } from "react";
export type Camera = {
offset: {
x: number;
y: number;
};
zoom: number;
};
export function Canvas({ centered }: { centered?: boolean }) {
import { useApi } from '@/hooks/useApi';
import { useGame } from '@/hooks/useGame';
import { useMouse } from '@/hooks/useMouse';
import { useScreen } from '@/hooks/useScreen';
import { type Params } from '@/lib/Game';
import { useEffect, useRef } from 'react';


export function Canvas(params: Params) {
const api = useApi();
const ref = useRef<HTMLCanvasElement | null>(null);
const [camera, setCamera] = useState<Camera>({
offset: { x: 0, y: 0 },
zoom: 1,
});
const [curPos, curWorldPos] = useMouse(ref, camera);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const cursorScreen = useMouse(canvasRef)
const screen = useScreen();
const [isMouseDown, setIsMouseDown] = useState(false);

// CENTER THE CAMERA
const game = useGame();
useEffect(() => {
if (!api.scene) return;
if (!centered) {
if (camera.offset.x === 0 && camera.offset.y === 0) return;
setCamera((prev) => ({ ...prev, offset: { x: 50, y: 100 } }));
return;
if (!game) return;
function handleKeyDown(e: KeyboardEvent) {
if (!game) return;
// esacpe
if (e.key === 'Escape') {
game.togglePause();
}
// space
if (e.key === ' ') {
game.setSpriniting(true);
}
}
function handleKeyUp(e: KeyboardEvent) {
if (!game) return;
// space
if (e.key === ' ') {
game.setSpriniting(false);
}
}

function handleMouseDown(e: MouseEvent) {
if (!game) return;
if (e.button === 0) {
game.setSpriniting(true);
}
}
function handleMouseUp(e: MouseEvent) {
if (!game) return;
if (e.button === 0) {
game.setSpriniting(false);
}
}
const playerHead = api.me?.body[0];
if (!playerHead) return;
const screenPlayerHead = worldToScreen(playerHead, camera);
const newCameraOffset = {
x: screen.width / 2 - screenPlayerHead.x,
y: screen.height / 2 - screenPlayerHead.y,
};
setCamera((prev) => ({ ...prev, offset: newCameraOffset }));
}, [api.me, api.scene, camera, centered, screen]);

// MOVE THE PLAYER
useEffect(() => {
if (!api.scene || !api.me) return;

const movePlayer = throttle(() => {
if (!api.scene || !api.me) return;
const playerHead = api.me.body[0];
if (!playerHead) return;
const angle = Math.atan2(
curWorldPos.y - playerHead.y,
curWorldPos.x - playerHead.x,
);
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('mouseup', handleMouseUp);
}
}, [game]);

api.move({ angle, isSprinting: isMouseDown });
}, 1000 / TPS);
useEffect(() => {
if (!game) return;
game.setApi(api);
}, [api, game]);

movePlayer();
return () => {
movePlayer.cancel();
};
}, [api, curWorldPos.x, curWorldPos.y]);
useEffect(() => {
if (!game) return;
game.setCursor(cursorScreen);
}, [cursorScreen, game]);

// DRAW THE SCENE
useEffect(() => {
const canvas = ref.current;
if (!canvas) return;
const c = canvas.getContext("2d");
if (!game) return;
const c = canvasRef.current?.getContext('2d');
if (!c) return;
c.clearRect(0, 0, canvas.width, canvas.height);
game.setContext(c);
}, [game]);

// Draw a circle at the cursor position
c.beginPath();
const screenCurPos = worldToScreen(curWorldPos, camera);
c.arc(screenCurPos.x, screenCurPos.y, 10, 0, 2 * Math.PI);
// use red color
c.fillStyle = "red";
c.strokeStyle = "red";
c.fill();
useEffect(() => {
if (!game) return;
game.updateParams(params);
}, [game, params]);

// Draw a line from the player's head to the cursor
if (api.me) {
const playerHead = api.me.body[0];
if (playerHead) {
const screenHead = worldToScreen(playerHead, camera);
const screenCurPos = worldToScreen(curWorldPos, camera);
c.beginPath();
c.moveTo(screenHead.x, screenHead.y);
c.lineTo(screenCurPos.x, screenCurPos.y);
c.fillStyle = "red";
c.strokeStyle = "red";
c.stroke();
}
}
useEffect(() => {
if (!game) return;
game.setScreenSize(screen);
}, [game, screen]);

useEffect(() => {
if (!game) return;
if (!api.scene) return;
game.setScene(api.scene);
}, [api.scene, game]);

const screenOrigin = worldToScreen({ x: 0, y: 0 }, camera);
c.fillStyle = "transparent";
c.fillRect(0, 0, canvas.width, canvas.height);
c.rect(screenOrigin.x, screenOrigin.y, api.scene.width, api.scene.height);
c.strokeStyle = "black";
c.stroke();

// Draw all food
api.scene.food.forEach((food) => {
const screenFood = worldToScreen(food.position, camera);
c.beginPath();
c.arc(screenFood.x, screenFood.y, 7, 0, 2 * Math.PI); // Draw a circle for each food
// use green color
c.fillStyle = "green";
c.fill();
});

// Draw all orbs
api.scene.orbs.forEach((orb) => {
const screenOrb = worldToScreen(orb.position, camera);
c.beginPath();
c.arc(screenOrb.x, screenOrb.y, 3 * orb.size, 0, 2 * Math.PI); // Draw a circle for each orb
// use blue color
c.fillStyle = "blue";
c.fill();
});

// Draw all players
api.scene.players.forEach((player) => {
player.body.forEach((bodyPart) => {
const screenBodyPart = worldToScreen(bodyPart, camera);
c.beginPath();
c.arc(screenBodyPart.x, screenBodyPart.y, 10, 0, 2 * Math.PI);
c.fillStyle = player.color;
c.fill();
c.strokeStyle = player.color;
c.stroke();
});
});
}, [
api,
api.scene,
camera,
camera.offset.x,
camera.offset.y,
curPos,
curWorldPos,
curWorldPos.x,
curWorldPos.y,
]);

if (!api.scene) return "Scene not found";

return (
<canvas
onMouseUp={() => setIsMouseDown(false)}
onMouseDown={() => setIsMouseDown(true)}
ref={ref}
className="h-full w-full"
height={screen.height}
width={screen.width}
/>
);
return <canvas ref={canvasRef} className='w-full h-full' height={screen.height} width={screen.width} />;
}
6 changes: 2 additions & 4 deletions packages/client/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
"use client"

import { SharedStateProvider } from '@/lib/shared-state';
import "@/styles/globals.css";
import { Inter } from "next/font/google";
import {Metadata} from "next";

const inter = Inter({
subsets: ["latin"],
variable: "--font-sans",
});

export const metadata: Metadata = {
title: 'Viper Vortex',
}

export default function RootLayout({
children,
Expand Down
12 changes: 7 additions & 5 deletions packages/client/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"use client";

import { useApi } from "@/hooks/useApi";
import { Canvas } from "./canvas";
import { SVGProps, useState } from "react";
import Image from "next/image";
import { useState } from "react";
import logo from "../assets/logo.png";
import { Canvas } from './canvas';

export default function HomePage() {
const [serverUrl, setServerUrl] = useState<string>("http://localhost:4000");
const [isCentered, setIsCentered] = useState<boolean>(false);
const [username, setUsername] = useState("");
const api = useApi(username, serverUrl);
const api = useApi();

return (
<main className="dustBackground flex h-full flex-col items-center justify-center">
Expand All @@ -34,7 +34,7 @@ export default function HomePage() {
<label htmlFor="centered">centered</label>
</div>

<div className="absolute right-0 top-0 bg-gray-400 p-4">
<div className="absolute right-0 bg-gray-400 p-4">
<div className="font-bold">Score</div>
<ul>
{api.scene?.players.map((player) => (
Expand Down Expand Up @@ -82,7 +82,9 @@ function LoginComponent(props: {
/>

<button
onClick={props.api.connect}
onClick={()=>{
props.api.connect(props.serverUrl, props.username);
}}
disabled={!props.username || !props.serverUrl}
className="w-full rounded-full bg-red-500 py-2 text-2xl font-bold ring-4 ring-red-500/50 ring-offset-8 ring-offset-red-950 transition-transform hover:scale-105 disabled:bg-gray-500 disabled:ring-gray-500/50"
>
Expand Down
Loading

0 comments on commit 646d30f

Please sign in to comment.