Skip to content
Open
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
169 changes: 169 additions & 0 deletions _projects/2026-04-12-TransBlog.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "fc5fb345",
"metadata": {},
"source": [
"---\n",
"layout: post\n",
"codemirror: true\n",
"title: Sprint 5 Transition Teaching Lesson\n",
"permalink: /sprintingsnails/transitions\n",
"author: Sophie Haas\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "ad84857b",
"metadata": {},
"source": [
"Have you ever wanted to know how to add a transition screen to your game? Maybe when the game ends, you want to keep the player connected to the experience by flashing a massive:\n",
"\n",
"<h2 style=\"text-align: center; margin: 20px 0;\">\n",
"<strong style=\"color: #ff0000; text-shadow: 0 0 10px #ff0000; font-family: 'Arial Black', sans-serif;\">G A M E &nbsp; O V E R</strong>\n",
"</h2>\n",
"\n",
"...or build anticipation for the next challenge with a bold:\n",
"\n",
"<h2 style=\"text-align: center; margin: 20px 0;\">\n",
"<strong style=\"color: #ffd700; text-shadow: 0 0 10px #ffd700; font-family: 'Arial Black', sans-serif;\">L E V E L &nbsp; 1</strong>\n",
"</h2>\n",
"\n",
"Static games can feel \"stiff.\" Transition screens act as the theater curtain for your code—they hide the background logic, signal a change in state, and provide crucial feedback to the player.\n",
"\n",
"In this blog, we’re going to demonstrate how to use:\n",
"\n",
"Dynamic DOM Injection: Building UI elements on the fly.\n",
"\n",
"CSS Keyframe Animations: Adding movement and \"pulse\" to your screens.\n",
"\n",
"Logic Triggers: Timing your transitions to match game events."
]
},
{
"cell_type": "markdown",
"id": "e41d1cde",
"metadata": {},
"source": [
"<div style=\"display: flex; justify-content: center; margin-bottom: 30px;\">\n",
" <a href=\"{{site.baseurl}}/gamify/timmycounter.html\" style=\"text-decoration: none;\">\n",
" <div style=\"background: linear-gradient(45deg, #4a0b0b, #220505); color: white; padding: 15px 30px; border-radius: 8px; font-family: 'Bungee', cursive; border: 1px solid #ff0000; box-shadow: 0 4px 15px rgba(255, 0, 0, 0.3); text-transform: uppercase; letter-spacing: 1px;\">\n",
" View Kirby Maze Example\n",
" </div>\n",
" </a>\n",
"</div>\n",
"\n",
"# Understanding UI Transitions & Overlays\n",
"\n",
"A transition screen is a **UI Overlay** that sits on a higher \"layer\" than your game world. In game development, we call this the **HUD (Heads-Up Display)** or **UI Layer**.\n",
"\n",
"---\n",
"\n",
"### The Primary Jobs of a Transition Screen\n",
"* **State Signaling:** Letting the player know exactly what happened—whether they won, lost, or paused.\n",
"* **Input Blocking:** Creating a \"logical wall\" that prevents the player from moving or interacting with the game world while the screen is visible.\n",
"* **Scene Management:** Providing the \"Escape Hatch\"—buttons to restart the level or move to the next stage.\n",
"\n",
"---\n",
"\n",
"# How it Works in Your Code\n",
"Your implementation uses a **\"CSS-in-JS\"** approach. Since we are working in a Notebook environment, we can't easily jump between different HTML files, so we manipulate the **DOM** (the webpage structure) directly.\n",
"\n",
"### The \"Ghost\" Element\n",
"In your constructor, you create the `lossOverlay`. Crucially, you set `display: none`. This makes it a **ghost element**—it exists in the code’s memory and the page’s structure, but the player cannot see or touch it yet.\n",
"\n",
"> **Code Insight:**\n",
"> ```javascript\n",
"> lossOverlay.style.cssText = \"display:none; position:fixed; width:100%; height:100%; z-index:20000;\";\n",
"> ```\n",
"\n",
"---\n",
"\n",
"# The Trigger (The \"Watcher\")\n",
"To make the screen appear, you need a **\"Watcher.\"** In your code, this is the `setInterval` timer that constantly evaluates the state of the game.\n",
"\n",
"| Step | Component | Logic |\n",
"| :--- | :--- | :--- |\n",
"| **1** | **The Condition** | The Watcher checks: `window.timeLeft <= 0` |\n",
"| **2** | **The Action** | The Watcher flips the CSS from `display: none` to `display: flex`. |\n",
"| **3** | **The Result** | The screen \"appears\" instantly, covering the game. |\n",
"\n",
"---\n",
"\n",
"# Key Concepts in the \"Kirby Maze\"\n",
"The implementation uses three advanced techniques to make the transition feel professional:\n",
"<img width=\"195\" height=\"91\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/27c2a77d-882a-4bb2-9aba-7f1457d8a344\" />\n",
"<img width=\"798\" height=\"434\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/8577b221-5000-4dc9-bb3e-a482530ec868\" />\n",
"### 1. Z-Indexing\n",
"By setting `z-index: 20000`, we ensure the Game Over screen stays above Kirby, the walls, and the background. It is mathematically the \"top-most\" item on the page.\n",
"\n",
"### 2. Input Gating\n",
"We use a `window.isPaused` variable as a \"gatekeeper.\" Even if the player mashes the **WASD** keys during the Game Over screen, the code checks this variable and refuses to move the character.\n",
"\n",
"### 3. Visual Polish\n",
"We injected a `<style>` tag with `@keyframes flash`. This allows the transition screen to have dynamic, moving parts (like flashing text) without needing a heavy video file or extra assets.\n",
"\n",
"---\n",
"\n",
"### Pro-Tip\n",
"Always ensure your transition screens have a clear **Call to Action** (like a \"Restart\" button). A transition screen without a way out is just a frozen game!\n",
"\n",
"### Where to implement this in your code\n",
"In our example level, we implemented our transition code in our window.addEventListener."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2b6ee08",
"metadata": {
"vscode": {
"languageId": "javascript"
}
},
"outputs": [],
"source": [
" window.addEventListener(\"load\", () => {\n",
" const lossOverlay = document.createElement(\"div\");\n",
" lossOverlay.style.cssText = \"position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.95); display:none; justify-content:center; align-items:center; z-index:20000; flex-direction:column; text-align:center;\";\n",
" lossOverlay.innerHTML = `\n",
" <h1 class=\"huge-game-over\">GAME OVER</h1>\n",
" <p class=\"flashing-text\">CLICK BELOW TO RESTART SYSTEM</p>\n",
" <button onclick=\"location.reload()\" style=\"\n",
" padding:20px 40px; \n",
" cursor:pointer; \n",
" background:#ff0000; \n",
" color:white; \n",
" border:4px solid white; \n",
" font-size:80px; \n",
" font-family:'Arial Black'; \n",
" border-radius:10px; \n",
" box-shadow: 0 0 20px rgba(255,0,0,0.6);\n",
" \">TRY AGAIN</button>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
113 changes: 113 additions & 0 deletions _projects/2026-04-22-multiblog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
layout: post
codemirror: True
title: Sprint 5 Multiplayer Lesson
permalink: /sprinting snails/multiplayer
author: Salma Zaghloul
---
# Multiplayer Games
## What are they?
In our context, multiplayer games are our familair .js levels, however more than one player can connect to play at the same time. Think of Among Us, or Fortnite.
---
## How does it work, exactly?
Our game file is connected to an external server in the backend, that is constantly communicating and exchanging information with the front end. The server handles connections, player counts, diconnections, and most importnantly, player identification.

### Connection
When a player connects, the server assigns them a randomly generated ID (SID).

```py
@socketio.on('connect')
def handle_connect():
global playercount
sid = request.sid
# Spawn at random or default position
players[sid] = {"x": 100, "y": 100,}
playercount = playercount + 1
print(f"[SERVER] Player joined: {sid} and count = " + f" {playercount}")
socketio.emit('player_update', {"players": players})
```
Notes:
- The server sets 'playercount' to a global variable, to allow communication between the front and backends.
- Explanation: If the variable is local, it cannot be printed, sent, etc.
- In the instance someone joins, the server requests and generates a new id and assigns it to the player.
- The server then increases the player count, before printing it in the console.
![alt text](<Screenshot 2026-04-22 101721.png>)

### Disconnection
Similar to player connection, the server must handle the event of a player disconnecting.
```py
@socketio.on('disconnect')
def handle_disconnect():
sid = request.sid
if sid in players:
del players[sid]
print(f"[SERVER] Player disconnected: {sid} and count = " f"{playercount}")
print(f"{playercount}")
socketio.emit('player_left', {"sid": sid})
socketio.emit('player_update', {"players": players})
```
Notes:
- In the event of a disconnection, the server looks up the "identification" of the player.
- It then deletes this person's SID
- This action effectively lowers the player count on its own, so there is no need to include it in this string.
---
## How do I add this to my game?
- First, update the .md file that generates your game level to contain this:
```
---
layout: opencs
title: Multiplayer
permalink: /gamify/mylevel
socket_io: true
---
```

- Then, implement the server into your .js file. Here's ours as an example if needed:
[]()
<button onclick="window.location.href="">Multiplayer.js</button>
<div class="button-container" id="btns">
<a href="https://mgithub.com/Salma-Zag/Tri2team/blob/main/assets/js/projects/multiplayer/levels/GameLevelMultiplayer.js'; class="button large btn-s5">SPRINT 5</a>
</div>

<div class="button-container" id="btns">
<a href="https://github.com/Salma-Zag/Tri2team/blob/main/assets/js/projects/multiplayer/levels/GameLevelMultiplayer.js" class="button large btn-start">Button</a>
</div>


Under your *const* definitions, add these strings of code.

```js
// const socket = io("wss://flask-ws.opencodingsociety.com", { transports: ["websocket"] });
const socket = io("ws://localhost:8590", { transports: ["websocket"] });

let myid = null;

socket.on("connect", () => {
console.log("connected:", socket.id);
});
```
IMPORTANT TO NOTE:
This line, "const socket = io("ws://localhost:8590", { transports: ["websocket"] });" is for local host use. When committing to deployed use, comment out this previous line and uncomment the one above it ("const socket = io("wss://flask-ws.opencodingsociety.com", { transports: ["websocket"] });").

For context, when you deploy your level, this portion of code should now look like:
```js
const socket = io("wss://flask-ws.opencodingsociety.com", { transports: ["websocket"] });
// const socket = io("ws://localhost:8590", { transports: ["websocket"] });

let myid = null;

socket.on("connect", () => {
console.log("connected:", socket.id);
});
```

## Further Developements
Our game level and engine are dynamic and are bound to update in the future. Keep an eye out!

Additional features:
- Physical sprites per player
- Notifying the host when a player joins
- Lobbies (to help people find one another)
- Lobby codes
- Levels
- Gravity (jump logic, velocity)
Loading