Skip to content

Commit

Permalink
blob mmo
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewmeeks committed Nov 30, 2023
1 parent 3d217be commit 447ff15
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 67 deletions.
53 changes: 26 additions & 27 deletions blob/index.html
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Blob</title>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="styles.css" />
</head>

<head>
<title>Blob</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="styles.css">
</head>

<body>
<div id="container" class="container">
<template id="char-tmpl">
<div class="character">
<div class="shadow"></div>
<div class="body"></div>
<div class="face">
<div class="left-eye">
<div class="iris">
<div class="pupil"></div>
<body>
<div id="container" class="container">
<template id="char-tmpl">
<div class="character">
<div class="shadow"></div>
<div class="body"></div>
<div class="face">
<div class="left-eye">
<div class="iris">
<div class="pupil"></div>
</div>
</div>
</div>
<div class="right-eye">
<div class="iris">
<div class="pupil"></div>
<div class="right-eye">
<div class="iris">
<div class="pupil"></div>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
<script type="text/javascript" src="script.js"></script>
</body>

</template>
<button id="btn-appearance">Change Appearance</button>
</div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
203 changes: 163 additions & 40 deletions blob/script.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,75 @@
/**
* @typedef {Object} SetTarget
* @prop {number} x
* @prop {number} y
*/

/**
* @typedef {Object} ChangeAppearance
* @prop {number} body_hue
* @prop {number} iris_hue
*/

/**
* @typedef {Object} SyncRot
* @prop {number} rot
*/

/**
* @typedef {Object} SetTargetReq
* @prop {SetTarget} SetTarget
*/

/**
* @typedef {Object} ChangeAppearanceReq
* @prop {ChangeAppearance} ChangeAppearance
*/

/**
* @typedef {Object} SyncRotReq
* @prop {SyncRot} SyncRot
*/

/**
* @typedef {SetTargetReq | ChangeAppearanceReq} ClientRequest
*/

/**
* @typedef {Object} ClientLeftRes
* @prop {Object} ClientLeft
*/

/**
* @typedef {Object} ClientJoinedRes
* @prop {ClientJoined} ClientJoined
*/

/**
* @typedef {Object} ClientJoined
* @prop {ClientState} state
* @prop {boolean} is_local
*/

/**
* @typedef {Object} ClientResponse
* @prop {number} id
* @prop {ClientRequest | ClientLeftRes | ClientJoinedRes} msg
*/

/**
* @typedef {Object} ClientState
* @prop {number} x
* @prop {number} y
* @prop {number} tx
* @prop {number} ty
* @prop {number} rot
* @prop {number} body_hue
* @prop {number} iris_hue
*/

/**
* @type {Object.<string, ClientState>}
*/
const states = {};

let scale = 1.0;
Expand All @@ -12,24 +84,44 @@ visualViewport.onresize = () => {
const sx = document.body.clientWidth / 1280;
const sy = document.body.clientHeight / 720;
setScale(Math.min(sx, sy));
}
};
visualViewport.onresize();

/**
* @param {string} id
* @param {ClientState} state
*/
function spawnCharacter(id, state) {
const tmpl = document.getElementById("char-tmpl");
const elemFrag = tmpl.content.cloneNode(true);
const charElem = elemFrag.querySelector(".character");
charElem.id = `char-${id}`;
charElem.style.setProperty("--hue", state.hue);
charElem.style.setProperty("--iris-hue", state.irisHue);
viewportElem.appendChild(elemFrag);
blinkLoop(id);
states[id] = structuredClone(state);
changeAppearance(id, state);
charElem.style.setProperty("--rotation", `${state.rot}deg`);
}

/**
* @param {string} id
* @param {ChangeAppearance} state
*/
function changeAppearance(id, state) {
const charElem = getCharElem(id);
charElem.style.setProperty("--hue", state.body_hue);
charElem.style.setProperty("--iris-hue", state.iris_hue);
}

function despawnCharacter(id) {
delete states[id];
document.getElementById(`char-${id}`).remove();
}

function getCharElem(id) {
const elem = document.getElementById(`char-${id}`);
if (!elem) throw new Error("Could not find element for character with ID " + id);
if (!elem)
throw new Error("Could not find element for character with ID " + id);
return elem;
}

Expand All @@ -47,63 +139,41 @@ function blink(id) {
}

function blinkLoop(id) {
if (!(id in states)) return;
blink(id);
setTimeout(() => blinkLoop(id), Math.random() * 10000);
}

function setPosition(id, x, y) {
const elem = getCharElem(id);
elem.style.setProperty("--pos-x", `${x}px`);
elem.style.setProperty("--pos-y", `${y - 30}px`);
elem.style.setProperty("--pos-y", `${y - 50}px`);
elem.style.zIndex = Math.round(y);
}

function createState(pos, rot, hue, irisHue) {
return {
x: pos[0],
y: pos[1],
tx: pos[0],
ty: pos[1],
rot,
hue,
irisHue
};
}


const localId = 0;
// spawnCharacter(localId, createState([150, 150], 0, 120, Math.random() * 360));

for (let i = 0; i < 10; i++) {
spawnCharacter(i, createState([Math.random() * 1280, Math.random() * 720], Math.random() * 360, Math.random() * 360, Math.random() * 360));
}

let prevEt = 0;
const speed = 0.1;
let localId;
function updateOuter() {
requestAnimationFrame((et) => {
const dt = et - prevEt;

for (const idStr in states) {
const id = Number(idStr);
// console.log(id);
const state = states[idStr];
for (const id in states) {
const state = states[id];
let dx = state.tx - state.x;
let dy = state.ty - state.y;

let dl = Math.sqrt(dx * dx + dy * dy);

if (dl <= speed * dt) {
if (localId === id && (state.x !== state.tx || state.y !== state.ty)) {
sendMessage({ SyncRot: { rot: state.rot } });
}
state.x = state.tx;
state.y = state.ty;

if (id !== localId && Math.random() > 0.99) {
state.tx = Math.random() * 1280;
state.ty = Math.random() * 720;
}
} else {
state.x += dx / dl * dt * speed;
state.y += dy / dl * dt * speed;
state.x += (dx / dl) * dt * speed;
state.y += (dy / dl) * dt * speed;
state.rot += dt * 0.3 * (dx / dl);
getCharElem(id).style.setProperty("--rotation", `${state.rot}deg`);
}
Expand All @@ -117,8 +187,61 @@ function updateOuter() {

updateOuter();

document.onclick = (ev) => {
console.log(ev);
states[localId].tx = ev.x / scale;
states[localId].ty = ev.y / scale;
/**
* @param {ClientRequest} msg
*/
function sendMessage(msg) {
ws.send(JSON.stringify(msg));
}

viewportElem.onclick = (ev) => {
sendMessage({
SetTarget: {
x: Math.round(ev.x / scale),
y: Math.round(ev.y / scale),
},
});
};

const ws = new WebSocket("ws://abc.matthewmeeks.xyz:8088/ws");
ws.onmessage = (ev) => {
/**
* @type {ClientResponse}
*/
const response = JSON.parse(ev.data);

if ("ClientJoined" in response.msg) {
spawnCharacter(response.id, {
...response.msg.ClientJoined.state,
tx: response.msg.ClientJoined.state.x,
ty: response.msg.ClientJoined.state.y,
});
if (response.msg.ClientJoined.is_local) {
localId = response.id;
}
} else if ("SetTarget" in response.msg) {
const state = states[response.id];
if (!state) return; // TODO
state.tx = response.msg.SetTarget.x;
state.ty = response.msg.SetTarget.y;
} else if ("ClientLeft" in response.msg) {
despawnCharacter(response.id);
} else if ("ChangeAppearance" in response.msg) {
changeAppearance(response.id, response.msg.ChangeAppearance);
} else if ("SyncRot" in response.msg) {
const state = states[response.id];
if (!state) return; // TODO
state.rot = response.msg.SyncRot.rot;
getCharElem(response.id).style.setProperty("--rotation", `${state.rot}deg`);
}
};

document.getElementById("btn-appearance").onclick = (ev) => {
sendMessage({
ChangeAppearance: {
body_hue: Math.trunc(Math.random() * 360),
iris_hue: Math.trunc(Math.random() * 360),
},
});
ev.stopPropagation();
};

0 comments on commit 447ff15

Please sign in to comment.